Compare commits
765 Commits
photos-v1.
...
auth-v4.4.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fb30ed477 | ||
|
|
91ea5763fb | ||
|
|
f29f9f2a94 | ||
|
|
5138898d73 | ||
|
|
171be22113 | ||
|
|
6a3575feb4 | ||
|
|
4e80c82a6f | ||
|
|
0c0acd9592 | ||
|
|
26384513f2 | ||
|
|
acd05e0a55 | ||
|
|
91a48943b7 | ||
|
|
a8111eab04 | ||
|
|
0084a3cf59 | ||
|
|
82e1fd3b0a | ||
|
|
d57c68188c | ||
|
|
65a7a49d07 | ||
|
|
631c46681d | ||
|
|
023d8ab8b3 | ||
|
|
a1e3cdd5d2 | ||
|
|
b1051bbd47 | ||
|
|
8682e3338b | ||
|
|
ee96b44b74 | ||
|
|
e992db4846 | ||
|
|
686d04339d | ||
|
|
5d0a86c248 | ||
|
|
2a375e56c3 | ||
|
|
ce3b6b1c1f | ||
|
|
52451cd0af | ||
|
|
03a25c2625 | ||
|
|
f425dc6eaf | ||
|
|
26d111da42 | ||
|
|
7b100e8dae | ||
|
|
b5d059c61b | ||
|
|
70c3c1d541 | ||
|
|
01b05d243d | ||
|
|
b560e5b71a | ||
|
|
625b562229 | ||
|
|
c8d31b3a7e | ||
|
|
026669b0d0 | ||
|
|
741aed7565 | ||
|
|
6ee58b8e0f | ||
|
|
61df740c01 | ||
|
|
3b3b41d55f | ||
|
|
0633582c7e | ||
|
|
1acd1f81f4 | ||
|
|
2b35677227 | ||
|
|
2b390b60c4 | ||
|
|
a33af20944 | ||
|
|
e8643c662e | ||
|
|
aea57dd212 | ||
|
|
fe33469a79 | ||
|
|
d660f71f56 | ||
|
|
61eb7c0e99 | ||
|
|
e438e35ccd | ||
|
|
d01348414c | ||
|
|
2faa616cab | ||
|
|
78bad4e5a5 | ||
|
|
466bb40a30 | ||
|
|
e55dcff9a5 | ||
|
|
8904916770 | ||
|
|
f3d19155a0 | ||
|
|
75440e7c05 | ||
|
|
7a7a50901f | ||
|
|
09ff43a1ef | ||
|
|
7cf4c7bf74 | ||
|
|
a3341202a7 | ||
|
|
c87b2b7542 | ||
|
|
2665d98681 | ||
|
|
8dd6e7d8ed | ||
|
|
594c18db79 | ||
|
|
320376e98d | ||
|
|
dbca5222e5 | ||
|
|
d06c9174ac | ||
|
|
962fa33b04 | ||
|
|
c163c8ae71 | ||
|
|
29720b350c | ||
|
|
b70cfe1309 | ||
|
|
cc5b2697d9 | ||
|
|
1a49770e0b | ||
|
|
98137e87ab | ||
|
|
fa0c70d633 | ||
|
|
909695ffaa | ||
|
|
b679d0213f | ||
|
|
995342bbdb | ||
|
|
0786a355ca | ||
|
|
9683888d82 | ||
|
|
3ecaf9a0dc | ||
|
|
c985fe9882 | ||
|
|
9b5dcc9a95 | ||
|
|
17d6860faa | ||
|
|
63a3edd053 | ||
|
|
2764256a02 | ||
|
|
bcc0403eb6 | ||
|
|
da0d9dfdab | ||
|
|
5171518d9b | ||
|
|
27d0c8a838 | ||
|
|
76d7f039ea | ||
|
|
1afc2e31bf | ||
|
|
d5a75707f3 | ||
|
|
923a2a60fe | ||
|
|
d47c96aa3f | ||
|
|
13ed68c79e | ||
|
|
79f5d6763c | ||
|
|
68aaedc2d9 | ||
|
|
2455d34553 | ||
|
|
f045dc8e04 | ||
|
|
7986563443 | ||
|
|
114dfb1f41 | ||
|
|
7ef65568d0 | ||
|
|
5a241a8153 | ||
|
|
cbc7c936f7 | ||
|
|
e14510f46e | ||
|
|
f5478c7396 | ||
|
|
ea1319b13b | ||
|
|
b45e270475 | ||
|
|
cdd82707f7 | ||
|
|
db33e61af9 | ||
|
|
36dda95b41 | ||
|
|
61b15ba3ef | ||
|
|
0690a32d59 | ||
|
|
463ce4964b | ||
|
|
ed05269a42 | ||
|
|
d537b8f00b | ||
|
|
84912c1a0e | ||
|
|
15f427ef37 | ||
|
|
89837b79e8 | ||
|
|
7afb305dbb | ||
|
|
8e6ff3b96c | ||
|
|
deb6cfe03a | ||
|
|
dbec318f7b | ||
|
|
6f24109669 | ||
|
|
beda124d3f | ||
|
|
89cd360f93 | ||
|
|
f1274afdd4 | ||
|
|
4f21f1e94e | ||
|
|
039866cf3b | ||
|
|
c4b860a8fe | ||
|
|
1d1e01898f | ||
|
|
53f947b5f0 | ||
|
|
1e3a112c35 | ||
|
|
3dc23092a4 | ||
|
|
fd65e81079 | ||
|
|
eec0480618 | ||
|
|
fac5ab5079 | ||
|
|
297d4bdbf5 | ||
|
|
9f9ad19d4b | ||
|
|
f16846b82e | ||
|
|
cf28fddfb3 | ||
|
|
2d8310460b | ||
|
|
0d5363c7a1 | ||
|
|
2f277bbffc | ||
|
|
cbf3340bf2 | ||
|
|
29f9a64bfb | ||
|
|
de918f42e6 | ||
|
|
6bd1547e09 | ||
|
|
aa70b2a437 | ||
|
|
13b74f387f | ||
|
|
d52e3894d8 | ||
|
|
780c2c2493 | ||
|
|
405c4d1258 | ||
|
|
5f498b01ee | ||
|
|
af5020e62c | ||
|
|
7ddf0f6fe1 | ||
|
|
9114fbca27 | ||
|
|
e25d71a7d4 | ||
|
|
1018765f7c | ||
|
|
0fb984d031 | ||
|
|
4ad3560387 | ||
|
|
482b175324 | ||
|
|
894d7382e8 | ||
|
|
53cc78d3e3 | ||
|
|
a634500e55 | ||
|
|
ee8ecd456c | ||
|
|
d69a22a73e | ||
|
|
8b1af42cf0 | ||
|
|
41e7d0056b | ||
|
|
d170789446 | ||
|
|
afbbde5f2b | ||
|
|
4e3112a4f6 | ||
|
|
a4950ece53 | ||
|
|
1a01d759b0 | ||
|
|
175b51fdb3 | ||
|
|
a4ba2edc54 | ||
|
|
1a8a26e9e4 | ||
|
|
a2ddcfd34f | ||
|
|
95700f52f6 | ||
|
|
fc16638bfe | ||
|
|
5d375eb837 | ||
|
|
77956d0f67 | ||
|
|
e651c1e328 | ||
|
|
5938e755ae | ||
|
|
6f7b3738b3 | ||
|
|
4ee9f45b3a | ||
|
|
c835a3d009 | ||
|
|
6d3e55a6d9 | ||
|
|
067c8b2a76 | ||
|
|
d355d18acb | ||
|
|
2baf3a3dd7 | ||
|
|
854610dd48 | ||
|
|
61be57fef5 | ||
|
|
8d59d7e254 | ||
|
|
1bcf728b3a | ||
|
|
5797be3460 | ||
|
|
20f50e4816 | ||
|
|
5064ebf4d3 | ||
|
|
2e0a2802e7 | ||
|
|
7868c2e16e | ||
|
|
ace5dc04e2 | ||
|
|
b0d940e65b | ||
|
|
5b8472b5a9 | ||
|
|
6bb7627d47 | ||
|
|
b24b5893ae | ||
|
|
9716ff80c4 | ||
|
|
2808f72233 | ||
|
|
5dd097ee09 | ||
|
|
7afdfe6ed9 | ||
|
|
8ff8981b76 | ||
|
|
b89f247f42 | ||
|
|
c3e5a037c0 | ||
|
|
70a1894071 | ||
|
|
42453675b2 | ||
|
|
185759d234 | ||
|
|
860760784a | ||
|
|
de10292a84 | ||
|
|
95f10e5a45 | ||
|
|
eabacf24ad | ||
|
|
61ffcfdc93 | ||
|
|
f73fbf4b60 | ||
|
|
0ade0a2807 | ||
|
|
1e2fb13908 | ||
|
|
8ab01aefe5 | ||
|
|
fa33a1afd9 | ||
|
|
856e126bc8 | ||
|
|
0f7aae2017 | ||
|
|
c1ddb863ad | ||
|
|
845d014945 | ||
|
|
7d52e3d852 | ||
|
|
9a647d6f78 | ||
|
|
6e99206523 | ||
|
|
d7af21aa84 | ||
|
|
ced1f6e164 | ||
|
|
7391f27967 | ||
|
|
4ea3989a33 | ||
|
|
641a99b823 | ||
|
|
7b48dbc1ad | ||
|
|
54c69e7aa5 | ||
|
|
f8bd8c9955 | ||
|
|
f7bb9d5974 | ||
|
|
60246be861 | ||
|
|
e3b72fc929 | ||
|
|
92dae44a0a | ||
|
|
d71016500a | ||
|
|
9c26f4040a | ||
|
|
79e048b4b7 | ||
|
|
5c0ce038d1 | ||
|
|
331a65d2a0 | ||
|
|
6c6ab8f463 | ||
|
|
441a884314 | ||
|
|
3372d83c5d | ||
|
|
0cf50513cc | ||
|
|
7ccf473190 | ||
|
|
c1f7a01ed2 | ||
|
|
6969385089 | ||
|
|
87a1c9417e | ||
|
|
6d13ff5151 | ||
|
|
0b5317867f | ||
|
|
6c9107301c | ||
|
|
a32c8116a2 | ||
|
|
d8cd81c702 | ||
|
|
886ab6d106 | ||
|
|
d17296216c | ||
|
|
55ee8b90b9 | ||
|
|
e003c783f5 | ||
|
|
379d2487bd | ||
|
|
8cdbb737dc | ||
|
|
a72041b8ba | ||
|
|
3b60f4954b | ||
|
|
b2ea248a5c | ||
|
|
54e8d6392d | ||
|
|
afa9e03743 | ||
|
|
a4eaf04a33 | ||
|
|
d30f0fba04 | ||
|
|
fb16346b0d | ||
|
|
41b1638838 | ||
|
|
702b3a8868 | ||
|
|
3572c4328d | ||
|
|
1c2b8061dc | ||
|
|
a9edcead06 | ||
|
|
372af94da4 | ||
|
|
192905b21e | ||
|
|
52a533a1e1 | ||
|
|
0314e94359 | ||
|
|
eb29c48f0e | ||
|
|
8bc701d104 | ||
|
|
84dda89e15 | ||
|
|
ff26dd5652 | ||
|
|
b68d95d481 | ||
|
|
459540fe7a | ||
|
|
2255ea1b92 | ||
|
|
ac704f1082 | ||
|
|
4db2e42ee3 | ||
|
|
f5949f5bd4 | ||
|
|
c4feb4b764 | ||
|
|
29bab5705b | ||
|
|
2ebeed3b6f | ||
|
|
a0dbcd3dbe | ||
|
|
81137652d4 | ||
|
|
4e1418b11a | ||
|
|
91268341be | ||
|
|
fb8fc051a9 | ||
|
|
d99914c4e9 | ||
|
|
736b3fc613 | ||
|
|
ef7f45aa3d | ||
|
|
4dc741151b | ||
|
|
e4471af4cb | ||
|
|
057df349b7 | ||
|
|
24c7b49132 | ||
|
|
5df009c7c7 | ||
|
|
1d276c795c | ||
|
|
bc762b972f | ||
|
|
36e4c06dd6 | ||
|
|
ceb3d3fe42 | ||
|
|
1dc806d270 | ||
|
|
8d03df5c36 | ||
|
|
a1ef8d33d3 | ||
|
|
4f347c1afd | ||
|
|
8171d56168 | ||
|
|
7e7751b5be | ||
|
|
d19a0fccda | ||
|
|
5b38ef394b | ||
|
|
ad87470c25 | ||
|
|
67140fe7f2 | ||
|
|
b372ba47ba | ||
|
|
24c66a9b6b | ||
|
|
201ef60f07 | ||
|
|
89294f2a76 | ||
|
|
4772557f7a | ||
|
|
073d2c5684 | ||
|
|
e022e7ae5b | ||
|
|
2cdeb88b4d | ||
|
|
78e70a1c05 | ||
|
|
ca319e501e | ||
|
|
05898dfbe2 | ||
|
|
c129cc15b5 | ||
|
|
a683883733 | ||
|
|
69b575cc66 | ||
|
|
63a4972839 | ||
|
|
6188578d18 | ||
|
|
5a4d8950af | ||
|
|
5007204944 | ||
|
|
4a19fc077e | ||
|
|
2b29f55587 | ||
|
|
4cc8ff2fb1 | ||
|
|
de29246304 | ||
|
|
8deb52301a | ||
|
|
16a20e8b0d | ||
|
|
3824bfbdd5 | ||
|
|
ef245e5c02 | ||
|
|
4dc6890afc | ||
|
|
961501a6fb | ||
|
|
914893eae6 | ||
|
|
7a10f4c145 | ||
|
|
092f64c3ca | ||
|
|
bdecb04398 | ||
|
|
e25418e5a6 | ||
|
|
a062c1ccc3 | ||
|
|
d1ae4d52dd | ||
|
|
3c532cd4f4 | ||
|
|
013389c696 | ||
|
|
9ca418d915 | ||
|
|
a52a3e5e57 | ||
|
|
2d739ef4de | ||
|
|
300300a8b9 | ||
|
|
be00cbaa51 | ||
|
|
43641b0b9e | ||
|
|
43cdd10e85 | ||
|
|
539145d38d | ||
|
|
a5ccae4390 | ||
|
|
08bb2f25fb | ||
|
|
55264445b2 | ||
|
|
bdadf8dc9c | ||
|
|
1c3428d89d | ||
|
|
34b53f8443 | ||
|
|
8448be6c68 | ||
|
|
6e4a856ea4 | ||
|
|
40ff361af1 | ||
|
|
11fae5e87c | ||
|
|
465e69b254 | ||
|
|
63b0cee589 | ||
|
|
f848fe0938 | ||
|
|
b7cd55aec3 | ||
|
|
aadab316f6 | ||
|
|
8bad8b87b1 | ||
|
|
1f7e74131b | ||
|
|
191db47d79 | ||
|
|
34621dd00f | ||
|
|
f44e90801f | ||
|
|
ded497d421 | ||
|
|
a3e48706de | ||
|
|
7f3d4db9a5 | ||
|
|
2709f69f2a | ||
|
|
2b32cd4277 | ||
|
|
57934f612e | ||
|
|
eae5e12c71 | ||
|
|
aef50f3f95 | ||
|
|
6772027c27 | ||
|
|
f386a0a72e | ||
|
|
9c94bcffaa | ||
|
|
6b703e9601 | ||
|
|
1c6e343994 | ||
|
|
949807bc97 | ||
|
|
fea46532f9 | ||
|
|
8824df29d4 | ||
|
|
2da2616ec1 | ||
|
|
2144f57ee0 | ||
|
|
476fe1b624 | ||
|
|
fb7fc53263 | ||
|
|
a842cdfe4e | ||
|
|
d5de1b5ce2 | ||
|
|
a1801435bd | ||
|
|
bc02238fd8 | ||
|
|
49ef8a693b | ||
|
|
6f585c3ad0 | ||
|
|
18f9f0048a | ||
|
|
6a7ba4156f | ||
|
|
30995b3b73 | ||
|
|
ff705280c7 | ||
|
|
6ff6594a81 | ||
|
|
69f12125ec | ||
|
|
862b11c530 | ||
|
|
e40afac212 | ||
|
|
8c66d9dac0 | ||
|
|
a880726f16 | ||
|
|
b2ab0679b4 | ||
|
|
d63ecf518e | ||
|
|
c5cdaf3e0b | ||
|
|
61f1cd6952 | ||
|
|
e6d4a779c5 | ||
|
|
14087ec5e9 | ||
|
|
15daa9e453 | ||
|
|
73728e60e0 | ||
|
|
5c925c4265 | ||
|
|
83bf336101 | ||
|
|
d5b9a0a92b | ||
|
|
40d84f10a8 | ||
|
|
59a534225c | ||
|
|
810ee3d9fe | ||
|
|
9605637e50 | ||
|
|
9016394ccc | ||
|
|
1c4ebcccb1 | ||
|
|
793fd5ba39 | ||
|
|
cae9988c9a | ||
|
|
853b916cf1 | ||
|
|
93d6f58660 | ||
|
|
7b145f0898 | ||
|
|
add09a601d | ||
|
|
cab711d88e | ||
|
|
c392bd2fd2 | ||
|
|
5dda596544 | ||
|
|
8101bee2fd | ||
|
|
c234bc7be8 | ||
|
|
27fd372d62 | ||
|
|
66f23283c1 | ||
|
|
8d7bc81c20 | ||
|
|
5e3ebd4a60 | ||
|
|
f4a4f71135 | ||
|
|
41e870f7a0 | ||
|
|
0342e1ef56 | ||
|
|
efed42ce4a | ||
|
|
185c5f5c43 | ||
|
|
6f5b33ea01 | ||
|
|
ef9d925686 | ||
|
|
d6a8373e5d | ||
|
|
e84f46f435 | ||
|
|
c1193be61c | ||
|
|
9b460ca1dc | ||
|
|
bd0ad0d1b4 | ||
|
|
d19322c1ae | ||
|
|
e71f5cecc7 | ||
|
|
622a368b45 | ||
|
|
72aec4bc5a | ||
|
|
c899725ed1 | ||
|
|
e15eb8d7c7 | ||
|
|
b38c49502b | ||
|
|
0fdee342eb | ||
|
|
0641ce389d | ||
|
|
f34ac356e9 | ||
|
|
120f9cef4d | ||
|
|
9b4247680a | ||
|
|
5d69376b54 | ||
|
|
64260896a0 | ||
|
|
2af46f62c8 | ||
|
|
a53701bc41 | ||
|
|
7a408a6242 | ||
|
|
6b9885c5d7 | ||
|
|
caa453d49d | ||
|
|
dd80acd4f4 | ||
|
|
5aa7682812 | ||
|
|
cb9a88e636 | ||
|
|
3a32659dd4 | ||
|
|
d5b5a26d9a | ||
|
|
d2980abb7a | ||
|
|
1a2f606d94 | ||
|
|
a101dba6cd | ||
|
|
ab2719a79c | ||
|
|
a614636789 | ||
|
|
48e9032ac1 | ||
|
|
1d2e18444c | ||
|
|
7f9e01a841 | ||
|
|
6933c77f36 | ||
|
|
61cacdddc0 | ||
|
|
936d578093 | ||
|
|
167a81f121 | ||
|
|
b550ee6b15 | ||
|
|
54feb7b2f9 | ||
|
|
f9dad575ec | ||
|
|
a39948ccc3 | ||
|
|
dfabd648d5 | ||
|
|
21e5bbb0fd | ||
|
|
10a0165a0f | ||
|
|
07c640cf90 | ||
|
|
1c099a60e8 | ||
|
|
efbdaef9ce | ||
|
|
65ddea0f7f | ||
|
|
ed15b47f1b | ||
|
|
6496660718 | ||
|
|
691d31d0a9 | ||
|
|
1f40901fd1 | ||
|
|
4982c1209e | ||
|
|
9170c80b26 | ||
|
|
1b4cb2ed99 | ||
|
|
45fe850afc | ||
|
|
6ae8abb492 | ||
|
|
f077213c62 | ||
|
|
92a8db53bd | ||
|
|
c812b80887 | ||
|
|
62c1f0c6ac | ||
|
|
cc486983ab | ||
|
|
a103bd4cca | ||
|
|
c56d9b312d | ||
|
|
6dda5d8762 | ||
|
|
3e9d78c866 | ||
|
|
c3eb55b258 | ||
|
|
0666bf56df | ||
|
|
fcd8298e10 | ||
|
|
62bd2d13d6 | ||
|
|
4263906c61 | ||
|
|
72acefadd4 | ||
|
|
2be8db783c | ||
|
|
f8e90e765f | ||
|
|
9424d26f55 | ||
|
|
bb352f3266 | ||
|
|
85d55c7d26 | ||
|
|
6062c20251 | ||
|
|
8861e0562f | ||
|
|
5e4de0793a | ||
|
|
80d36b5a91 | ||
|
|
8e038dda14 | ||
|
|
a5eba40f29 | ||
|
|
d98d220019 | ||
|
|
ce6b0da630 | ||
|
|
d1d91338af | ||
|
|
790c022730 | ||
|
|
be65f0fba8 | ||
|
|
3677d53ea9 | ||
|
|
f92101eaf8 | ||
|
|
1d3ca81308 | ||
|
|
e9f22cff93 | ||
|
|
d5740c4b66 | ||
|
|
07bd28381d | ||
|
|
8c8c9d7ffa | ||
|
|
d06016dddc | ||
|
|
f4531ef088 | ||
|
|
56772521e0 | ||
|
|
244ec87a9a | ||
|
|
043d0936ef | ||
|
|
9129910981 | ||
|
|
4194c53d08 | ||
|
|
9d5960c6fe | ||
|
|
f088c24abe | ||
|
|
e197423c1d | ||
|
|
b22bc93e57 | ||
|
|
236301dc16 | ||
|
|
b4a60fd2f4 | ||
|
|
079ac37d1f | ||
|
|
3deb138b77 | ||
|
|
5b9b328c99 | ||
|
|
e541f0522d | ||
|
|
93413687c9 | ||
|
|
48ef4df187 | ||
|
|
96d748dc87 | ||
|
|
9d28fa99a3 | ||
|
|
f9cce787f7 | ||
|
|
dcc02db296 | ||
|
|
c0fd71a668 | ||
|
|
116f22a853 | ||
|
|
501b541134 | ||
|
|
1785baf4af | ||
|
|
b08d8de1c8 | ||
|
|
0284287c9c | ||
|
|
be2665f57f | ||
|
|
b721a84889 | ||
|
|
442d6526be | ||
|
|
08346e5bcd | ||
|
|
e35b4eac40 | ||
|
|
7c6da77c21 | ||
|
|
ab59a15769 | ||
|
|
45e07f3be0 | ||
|
|
a2df4fb48a | ||
|
|
5a2ba82fff | ||
|
|
6a907d0f7d | ||
|
|
20dfec2e67 | ||
|
|
96c3880e38 | ||
|
|
2adabc1e24 | ||
|
|
a6ca7a5792 | ||
|
|
6abea7ae6d | ||
|
|
429b6be368 | ||
|
|
ed9af710fe | ||
|
|
f8ef263e6d | ||
|
|
fae47f102e | ||
|
|
240bfdd296 | ||
|
|
672123d746 | ||
|
|
cc3f20831a | ||
|
|
a282f82909 | ||
|
|
2fde22272e | ||
|
|
d63da04392 | ||
|
|
7cd3f8e2ac | ||
|
|
9517514396 | ||
|
|
41a4f3d286 | ||
|
|
d1a2efff0e | ||
|
|
00d723ca7c | ||
|
|
47b2e0d0ab | ||
|
|
ac9dbd4ee1 | ||
|
|
8b6f6346be | ||
|
|
73ad6b45fb | ||
|
|
020f8cea23 | ||
|
|
d52accdcc9 | ||
|
|
975c7ae8d1 | ||
|
|
c4463d6251 | ||
|
|
e96888d2c9 | ||
|
|
03df804868 | ||
|
|
685e0099c8 | ||
|
|
4a924a7296 | ||
|
|
1f4beab764 | ||
|
|
4fd121adf1 | ||
|
|
a942e68d27 | ||
|
|
ece63f7a8d | ||
|
|
dcdc26e4e8 | ||
|
|
ffa8ccd0d3 | ||
|
|
01f612e450 | ||
|
|
70f80c7b57 | ||
|
|
ab875683ed | ||
|
|
638108a819 | ||
|
|
dec193599f | ||
|
|
4913746a31 | ||
|
|
0966ab7d19 | ||
|
|
83ae436008 | ||
|
|
8f2a2caac1 | ||
|
|
7cc6319d5f | ||
|
|
eabe213207 | ||
|
|
5b7f821a26 | ||
|
|
939ab4398c | ||
|
|
c4effd4ef4 | ||
|
|
bcf1a044cc | ||
|
|
2dd9a080c6 | ||
|
|
57a4e6dd15 | ||
|
|
93cd2d30e4 | ||
|
|
f65db123f2 | ||
|
|
d697f22896 | ||
|
|
65faa98621 | ||
|
|
e4a0125f1c | ||
|
|
98d06f975c | ||
|
|
d0ebe65af4 | ||
|
|
08d936be45 | ||
|
|
71596ebd8e | ||
|
|
8ededf54c7 | ||
|
|
0520fcd7ec | ||
|
|
b6173d6c1b | ||
|
|
6fa07e12db | ||
|
|
804dacef91 | ||
|
|
97350005c9 | ||
|
|
5f36a23f4e | ||
|
|
d336541d2c | ||
|
|
780ea55ca8 | ||
|
|
38663088b1 | ||
|
|
eb9987d2c0 | ||
|
|
cf4084380c | ||
|
|
691469ef5e | ||
|
|
8106bc6940 | ||
|
|
deb68d5cfc | ||
|
|
f4e158dab6 | ||
|
|
e8b0d9c25b | ||
|
|
43ce9edb95 | ||
|
|
3b4c7095c4 | ||
|
|
d95bb0785f | ||
|
|
d53281500b | ||
|
|
44199093f0 | ||
|
|
18442e25fc | ||
|
|
0fa7245144 | ||
|
|
4fc7347cb7 | ||
|
|
4840a44c4d | ||
|
|
327bda5b30 | ||
|
|
c3b1da2a7e | ||
|
|
d35f898b70 | ||
|
|
6730c0c682 | ||
|
|
c2c7ac8b23 | ||
|
|
2f670e316b | ||
|
|
f47a6f7b42 | ||
|
|
3513b51477 | ||
|
|
bd8fc08b7c | ||
|
|
ae925a240e | ||
|
|
adf1379b9e | ||
|
|
71d6aed1aa | ||
|
|
6627f77d92 | ||
|
|
14b70ce66e | ||
|
|
d9a5fbfe00 | ||
|
|
f816166743 | ||
|
|
3aba4fad47 | ||
|
|
60137585d1 | ||
|
|
59316c263f | ||
|
|
44682404ff | ||
|
|
83c864df2b | ||
|
|
184882fae2 | ||
|
|
e59e600a35 | ||
|
|
6f5c5a0b06 | ||
|
|
3d4ff93e65 | ||
|
|
d88621ab5a | ||
|
|
95e6a86b10 | ||
|
|
e5d63fe9e7 | ||
|
|
1265002d5a | ||
|
|
c7aecc9b30 | ||
|
|
dba837c62c | ||
|
|
e5bdd74fa9 | ||
|
|
0f31278965 | ||
|
|
be3e434bec | ||
|
|
9d9a7b548d | ||
|
|
3708819e6b | ||
|
|
389220357e | ||
|
|
d9925f29d8 | ||
|
|
0dab15b703 | ||
|
|
2449dbe0cd | ||
|
|
7f96a11e07 | ||
|
|
d7cb5c29cf | ||
|
|
399ecdfd7d | ||
|
|
f4580c8fdf | ||
|
|
13f78ecc19 | ||
|
|
9267c4012b | ||
|
|
a8e80717aa | ||
|
|
8e552c57bb | ||
|
|
28f03d3514 | ||
|
|
c7047ab964 | ||
|
|
daa3fcd354 | ||
|
|
0057e71e02 | ||
|
|
50fabee1e0 | ||
|
|
227d76db29 | ||
|
|
9207b0c7b8 | ||
|
|
9b8c48ca6e | ||
|
|
dc05e254cb | ||
|
|
f047757c63 | ||
|
|
f9711e09a1 | ||
|
|
a5644f292e | ||
|
|
f3a88dc3fa | ||
|
|
9ef2a5fc62 | ||
|
|
36ab2f05df | ||
|
|
723511540c |
29
.github/workflows/auth-release.yml
vendored
@@ -83,7 +83,7 @@ jobs:
|
||||
# disable this step if release tag contains nightly or beta
|
||||
if: startsWith(github.ref, 'refs/tags/auth-v') && !contains(github.ref, 'nightly') && !contains(github.ref, 'beta')
|
||||
run: |
|
||||
flutter build appbundle --release --flavor playstore --dart-define=app.flavor=playstore
|
||||
flutter build appbundle --release --flavor playstore --dart-define=app.flavor=playstore --dart-define=cronetHttpNoPlay=true
|
||||
env:
|
||||
SIGNING_KEY_PATH: "/home/runner/work/_temp/keystore/ente_auth_key.jks"
|
||||
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
|
||||
@@ -117,7 +117,9 @@ jobs:
|
||||
mv dist/**/*-*-linux.deb artifacts/ente-${{ github.ref_name }}-x86_64.deb
|
||||
|
||||
- name: Generate checksums
|
||||
run: sha256sum artifacts/ente-* >> artifacts/sha256sum-rpm-appimage
|
||||
run: |
|
||||
sha256sum artifacts/ente-auth-*.apk >> artifacts/sha256sum-apk
|
||||
sha256sum artifacts/ente-auth-*.deb artifacts/ente-auth-*.rpm artifacts/ente-auth-*.AppImage >> artifacts/sha256sum-linux
|
||||
|
||||
- name: Create a draft GitHub release
|
||||
uses: ncipollo/release-action@v1
|
||||
@@ -139,6 +141,7 @@ jobs:
|
||||
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
environment: "auth-win-build"
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -172,14 +175,22 @@ jobs:
|
||||
- name: Retain Windows EXE and DLLs
|
||||
run: cp -r build/windows/x64/runner/Release ente-${{ github.ref_name }}-windows
|
||||
|
||||
- name: Code sign Windows installer and EXE
|
||||
uses: dlemstra/code-sign-action@v1
|
||||
- name: Sign files with Trusted Signing
|
||||
uses: azure/trusted-signing-action@v0
|
||||
with:
|
||||
certificate: "${{ secrets.WINDOWS_CERTIFICATE }}"
|
||||
password: "${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }}"
|
||||
files: |
|
||||
auth/artifacts/ente-${{ github.ref_name }}-installer.exe
|
||||
auth/ente-${{ github.ref_name }}-windows/auth.exe
|
||||
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||
endpoint: ${{ secrets.AZURE_ENDPOINT }}
|
||||
trusted-signing-account-name: ${{ secrets.AZURE_CODE_SIGNING_NAME }}
|
||||
certificate-profile-name: ${{ secrets.AZURE_CERT_PROFILE_NAME }}
|
||||
files: |
|
||||
${{ github.workspace }}/auth/artifacts/ente-${{ github.ref_name }}-installer.exe
|
||||
${{ github.workspace }}/auth/ente-${{ github.ref_name }}-windows/auth.exe
|
||||
file-digest: SHA256
|
||||
timestamp-rfc3161: http://timestamp.acs.microsoft.com
|
||||
timestamp-digest: SHA256
|
||||
|
||||
|
||||
- name: Zip Windows EXE and DLLs
|
||||
run: tar.exe -a -c -f artifacts/ente-${{ github.ref_name }}-windows.zip ente-${{ github.ref_name }}-windows
|
||||
|
||||
70
.github/workflows/auth-win-sign.yml
vendored
Normal file
@@ -0,0 +1,70 @@
|
||||
name: "Windows build & Sign (auth)"
|
||||
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allow manually running the action
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
environment: "auth-win-build"
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: auth
|
||||
|
||||
steps:
|
||||
- name: Checkout code and submodules
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install Flutter ${{ env.FLUTTER_VERSION }}
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- name: Create artifacts directory
|
||||
run: mkdir artifacts
|
||||
|
||||
- name: Build Windows installer
|
||||
run: |
|
||||
flutter config --enable-windows-desktop
|
||||
# dart pub global activate flutter_distributor
|
||||
dart pub global activate --source git https://github.com/ente-io/flutter_distributor_fork --git-ref develop --git-path packages/flutter_distributor
|
||||
make innoinstall
|
||||
flutter_distributor package --platform=windows --targets=exe --skip-clean
|
||||
mv dist/**/*-windows-setup.exe artifacts/ente-${{ github.ref_name }}-installer.exe
|
||||
|
||||
- name: Retain Windows EXE and DLLs
|
||||
run: cp -r build/windows/x64/runner/Release ente-${{ github.ref_name }}-windows
|
||||
|
||||
- name: Sign files with Trusted Signing
|
||||
uses: azure/trusted-signing-action@v0
|
||||
with:
|
||||
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
|
||||
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}
|
||||
azure-client-secret: ${{ secrets.AZURE_CLIENT_SECRET }}
|
||||
endpoint: ${{ secrets.AZURE_ENDPOINT }}
|
||||
trusted-signing-account-name: ${{ secrets.AZURE_CODE_SIGNING_NAME }}
|
||||
certificate-profile-name: ${{ secrets.AZURE_CERT_PROFILE_NAME }}
|
||||
files: |
|
||||
${{ github.workspace }}/auth/artifacts/ente-${{ github.ref_name }}-installer.exe
|
||||
${{ github.workspace }}/auth/ente-${{ github.ref_name }}-windows/auth.exe
|
||||
file-digest: SHA256
|
||||
timestamp-rfc3161: http://timestamp.acs.microsoft.com
|
||||
timestamp-digest: SHA256
|
||||
|
||||
- name: Zip Windows EXE and DLLs
|
||||
run: tar.exe -a -c -f artifacts/ente-${{ github.ref_name }}-windows.zip ente-${{ github.ref_name }}-windows
|
||||
|
||||
- name: Generate checksums
|
||||
run: sha256sum artifacts/ente-* > artifacts/sha256sum-windows
|
||||
77
.github/workflows/mobile-internal-release-rust.yml
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
name: "Internal release (photos)"
|
||||
|
||||
on:
|
||||
workflow_dispatch: # Allow manually running the action
|
||||
|
||||
env:
|
||||
FLUTTER_VERSION: "3.24.3"
|
||||
RUST_VERSION: "1.85.1"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: mobile
|
||||
|
||||
steps:
|
||||
- name: Checkout code and submodules
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup JDK 17
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: 17
|
||||
|
||||
- name: Install Flutter ${{ env.FLUTTER_VERSION }}
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: "stable"
|
||||
flutter-version: ${{ env.FLUTTER_VERSION }}
|
||||
cache: true
|
||||
|
||||
- name: Install Rust ${{ env.RUST_VERSION }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ env.RUST_VERSION }}
|
||||
|
||||
- name: Install Flutter Rust Bridge
|
||||
run: cargo install flutter_rust_bridge_codegen
|
||||
|
||||
- name: Setup keys
|
||||
uses: timheuer/base64-to-file@v1
|
||||
with:
|
||||
fileName: "keystore/ente_photos_key.jks"
|
||||
encodedString: ${{ secrets.SIGNING_KEY_PHOTOS }}
|
||||
|
||||
- name: Build PlayStore AAB
|
||||
run: |
|
||||
flutter build appbundle --dart-define=cronetHttpNoPlay=true --release --flavor playstore
|
||||
env:
|
||||
SIGNING_KEY_PATH: "/home/runner/work/_temp/keystore/ente_photos_key.jks"
|
||||
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS_PHOTOS }}
|
||||
SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD_PHOTOS }}
|
||||
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD_PHOTOS }}
|
||||
|
||||
- name: Upload AAB to PlayStore
|
||||
uses: r0adkll/upload-google-play@v1
|
||||
with:
|
||||
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
|
||||
packageName: io.ente.photos
|
||||
releaseFiles: mobile/build/app/outputs/bundle/playstoreRelease/app-playstore-release.aab
|
||||
track: internal
|
||||
|
||||
- name: Notify Discord
|
||||
uses: sarisia/actions-status-discord@v1
|
||||
with:
|
||||
webhook: ${{ secrets.DISCORD_INTERNAL_RELEASE_WEBHOOK }}
|
||||
nodetail: true
|
||||
title: "🏆 Internal release available for Photos"
|
||||
description: "[Download](https://play.google.com/store/apps/details?id=io.ente.photos)"
|
||||
color: 0x00ff00
|
||||
@@ -63,6 +63,6 @@ jobs:
|
||||
with:
|
||||
webhook: ${{ secrets.DISCORD_INTERNAL_RELEASE_WEBHOOK }}
|
||||
nodetail: true
|
||||
title: "🏆 Internal release available for Photos"
|
||||
title: "🏆 Internal release Photos (Branch: ${{ github.ref_name }})"
|
||||
description: "[Download](https://play.google.com/store/apps/details?id=io.ente.photos)"
|
||||
color: 0x00ff00
|
||||
|
||||
@@ -43,7 +43,13 @@
|
||||
"title": "Anycoin Direct",
|
||||
"slug": "anycoindirect"
|
||||
},
|
||||
{
|
||||
{
|
||||
"title": "AR24",
|
||||
"altNames": [
|
||||
"Docaposte AR24"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Aruba",
|
||||
"slug": "aruba",
|
||||
"hex": "ef8a33"
|
||||
@@ -132,6 +138,10 @@
|
||||
"Binance US"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Bitazza",
|
||||
"slug": "bitazza"
|
||||
},
|
||||
{
|
||||
"title": "Bitkub",
|
||||
"slug": "bitkub"
|
||||
@@ -188,7 +198,9 @@
|
||||
"slug": "blue_sky"
|
||||
},
|
||||
{
|
||||
"title": "bonify"
|
||||
"title": "bonify",
|
||||
"slug": "bonify",
|
||||
"altNames": ["bonify.de"]
|
||||
},
|
||||
{
|
||||
"title": "Booking",
|
||||
@@ -299,6 +311,10 @@
|
||||
"title": "CSGORoll",
|
||||
"slug": "csgoroll"
|
||||
},
|
||||
{
|
||||
"title": "Cryptee",
|
||||
"slug": "cryptee"
|
||||
},
|
||||
{
|
||||
"title": "Cwallet",
|
||||
"altNames": [
|
||||
@@ -431,6 +447,9 @@
|
||||
"title": "Finanzfluss",
|
||||
"slug": "finanzfluss"
|
||||
},
|
||||
{
|
||||
"title": "Finary"
|
||||
},
|
||||
{
|
||||
"title": "Firefox",
|
||||
"slug": "mozilla"
|
||||
@@ -452,6 +471,9 @@
|
||||
"title": "Gate.io",
|
||||
"slug": "gateio.svg"
|
||||
},
|
||||
{
|
||||
"title": "GIRAD"
|
||||
},
|
||||
{
|
||||
"title": "GitHub"
|
||||
},
|
||||
@@ -733,6 +755,7 @@
|
||||
{
|
||||
"title": "Mistral",
|
||||
"altNames": [
|
||||
"Le Chat",
|
||||
"Mistral AI",
|
||||
"MistralAI"
|
||||
]
|
||||
@@ -877,11 +900,15 @@
|
||||
"欧易"
|
||||
]
|
||||
},
|
||||
{
|
||||
{
|
||||
"title": "OnShape",
|
||||
"slug": "onshape",
|
||||
"hex": "7abb5e"
|
||||
},
|
||||
{
|
||||
"title": "Oracle Cloud",
|
||||
"slug": "oracle_cloud"
|
||||
},
|
||||
{
|
||||
"title": "Parqet",
|
||||
"slug": "parqet"
|
||||
@@ -1001,7 +1028,7 @@
|
||||
"title": "RealMe",
|
||||
"slug": "realme"
|
||||
},
|
||||
{
|
||||
{
|
||||
"title": "RealVNC",
|
||||
"slug": "realvnc",
|
||||
"hex": "488aec"
|
||||
@@ -1355,4 +1382,4 @@
|
||||
"title": "CoinSpot"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
6
auth/assets/custom-icons/icons/ar24.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="500" height="500" viewBox="0 0 500 500" fill="#0000FF" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M139.63 306.55H125.64C123.41 306.55 121.53 306.5 119.99 306.39C118.45 306.28 117.13 305.99 116.01 305.51C114.9 305.04 113.92 304.32 113.08 303.36C112.22 302.41 111.38 301.09 110.53 299.39L103.85 286.35H35.47L25.29 306.55H0L58.37 194.11H81.26L139.63 306.55ZM93.2 265.36L69.66 218.6L46.12 265.36H93.2Z"/>
|
||||
<path d="M265.23 306.55H245.67C241.96 306.55 238.93 306.07 236.6 305.12C234.27 304.16 232.31 302.68 230.72 300.67L206.17 270.76H177.48V306.55H153.95V195.23C156.92 195.12 160.16 195.04 163.66 194.99C167.17 194.93 170.77 194.86 174.5 194.75C178.21 194.64 181.92 194.57 185.64 194.51C189.36 194.46 192.86 194.43 196.15 194.43C209.74 194.43 220.97 195.41 229.84 197.37C238.71 199.34 246 202.91 251.74 208.1C258.11 214.04 261.3 222.1 261.3 232.28C261.3 237.37 260.61 241.85 259.23 245.72C257.84 249.59 255.88 252.96 253.33 255.81C250.78 258.68 247.7 261.09 244.09 263.05C240.49 265.02 236.45 266.63 231.99 267.9L265.23 306.55ZM196.25 250.25C199.75 250.25 203.27 250.22 206.82 250.17C210.38 250.12 213.74 249.88 216.93 249.45C220.1 249.03 223.02 248.36 225.67 247.46C228.32 246.55 230.5 245.24 232.19 243.54C233.67 241.94 234.82 240.29 235.61 238.59C236.4 236.89 236.81 234.7 236.81 232.03C236.81 230.23 236.45 228.47 235.77 226.77C235.08 225.06 234.15 223.62 232.99 222.45C231.4 220.85 229.44 219.6 227.11 218.7C224.77 217.79 222.1 217.12 219.07 216.7C216.05 216.27 212.63 216.03 208.81 215.98C205 215.93 200.81 215.9 196.25 215.9H187.18C183.58 215.9 180.34 215.95 177.48 216.06V250.1C180.34 250.2 183.58 250.25 187.18 250.25H196.25Z"/>
|
||||
<path d="M324.59 213.35C318.34 213.35 312.61 215.03 307.41 218.37C302.22 221.7 297.93 225.65 294.54 230.22L276.09 217.97C282.13 210.65 289.36 204.66 297.79 200C306.22 195.33 315.47 193 325.55 193C332.33 193 338.53 193.87 344.15 195.62C349.77 197.37 354.59 199.84 358.63 203.02C362.65 206.2 365.78 210.07 368.01 214.63C370.23 219.19 371.35 224.27 371.35 229.89C371.35 235.2 370.36 239.86 368.4 243.89C366.44 247.92 363.85 251.6 360.61 254.94C357.38 258.28 353.69 261.38 349.56 264.25C345.42 267.11 341.18 269.97 336.84 272.84L317.27 285.72H370.87V306.55H279.42V286.04L318.39 260.27C322.2 257.73 325.86 255.32 329.36 253.03C332.86 250.76 335.93 248.42 338.58 246.04C341.24 243.65 343.35 241.13 344.95 238.48C346.54 235.83 347.33 232.91 347.33 229.74C347.33 227.41 346.72 225.23 345.5 223.22C344.28 221.2 342.64 219.45 340.57 217.97C338.51 216.49 336.09 215.35 333.34 214.55C330.58 213.75 327.66 213.35 324.59 213.35Z"/>
|
||||
<path d="M457.22 306.55V283.49H388.84V259.95L455.47 195.23H480.12V263.77H500V283.49H480.12V306.55L457.22 306.55ZM413.17 263.77H457.22V220.35L413.17 263.77Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
28
auth/assets/custom-icons/icons/bitazza.svg
Normal file
@@ -0,0 +1,28 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="836.84424" height="914.80933">
|
||||
<defs>
|
||||
<clipPath id="a">
|
||||
<path stroke-linecap="round" d="M-186.41-125.66h372.816v351.267H-186.41Z"/>
|
||||
</clipPath>
|
||||
<clipPath id="b">
|
||||
<path stroke-linecap="round" d="M-186.41-95.22h372.816v351.267H-186.41Z"/>
|
||||
</clipPath>
|
||||
<clipPath id="c">
|
||||
<path stroke-linecap="round" d="M-160.08-140.83h372.816v351.267H-160.08Z"/>
|
||||
</clipPath>
|
||||
<clipPath id="d">
|
||||
<path stroke-linecap="round" d="M-212.74-140.83h372.816v351.267H-212.74Z"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
<g clip-path="url(#a)" transform="translate(418.40805 457.4024)scale(3.64)">
|
||||
<path d="m298.246 129.84-6.486-4.585 6.486-4.586c12.26215-8.68661 19.24883-23.025924 18.53269-38.036075-.71613-15.010151-9.03624-28.619172-22.06969-36.098925l-.568-.349c-12.19994-7.033047-27.02434-7.819595-39.9-2.117l-7.053 3.144-.83-7.687c-1.51376-14.021838-9.62342-26.481265-21.832-33.542l-.2-.087C217.64616 2.0246361 210.0606-.00958995 202.34 0c-8.04957.00100343-15.94637 2.1978633-22.84 6.354l-1.114.677c-11.58345 7.090871-19.28369 19.094034-20.9 32.579l-.917 7.6-6.967-3.1c-12.8836-5.747507-27.74063-4.976398-39.96 2.074l-.7.394c-13.604075 7.890873-21.986349 22.420072-22.008 38.147v.787c.02516 14.283035 6.954274 27.67261 18.6 35.942L112 126.04l-6.463 4.586c-12.260313 8.67436-19.255033 22.99852-18.556462 38.00091.698572 15.00239 8.994121 28.61458 22.007462 36.11209l.633.393c12.22088 7.0137 27.04998 7.79342 39.939 2.1l7.054-3.123.807 7.687c1.51242 13.97659 9.57745 26.40246 21.727 33.475l.284.153c13.92748 8.0385 31.12909 7.84683 44.874-.5l1.092-.655c11.59433-7.07976 19.29833-19.08878 20.9-32.579l.9-7.6 6.987 3.123c12.9143 5.73028 27.78955 4.94317 40.027-2.118l.611-.372c13.60439-7.88085 21.98922-22.40383 22.012-38.126v-.808c-.0274-14.27871-6.94846-27.66532-18.583-35.943M292 166.591c-.0145 6.86067-3.67326 13.19693-9.608 16.639l-.654.394c-5.96032 3.4063-13.27768 3.4063-19.238 0l-17.884-10.351-22.841 13.211v20.133c-.0194 6.70609-3.51599 12.92214-9.237 16.421l-1.114.677c-5.99641 3.62641-13.48927 3.70989-19.565.218l-.24-.153c-5.94051-3.43919-9.60731-9.7748-9.63-16.639v-20.657l-22.841-13.211-17.884 10.351c-5.9447 3.40734-13.2503 3.40734-19.195 0l-.677-.415c-5.92257-3.40301-9.59053-9.69703-9.63171-16.52753-.0412-6.83049 3.5506-13.16829 9.43171-16.64247l18.648-11.027-.153-26.313-18.3-10.569c-5.94649-3.42296-9.61685-9.756715-9.63-16.618v-.787c.0184-6.865358 3.68634-13.20295 9.63-16.639l.655-.415c5.9528-3.406398 13.2642-3.406398 19.217 0l17.884 10.351 22.841-13.189V44.7c.0433-6.705114 3.54343-12.913272 9.258-16.421l1.114-.677c5.98556-3.637575 13.47843-3.721536 19.544-.219l.24.131c5.95616 3.434267 9.62726 9.785677 9.63 16.661v20.658l22.841 13.189L262.5 67.671c5.96032-3.406299 13.27768-3.406299 19.238 0l.654.415c5.91101 3.411809 9.56893 9.702792 9.61008 16.527659.0412 6.824866-3.54064 13.159502-9.41008 16.642341l-18.67 11.027.153 26.313 18.321 10.569c5.93573 3.43032 9.59519 9.76235 9.604 16.618z" style="opacity:1;fill:#10f48b;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0" transform="translate(-201.88 -125.66)" vector-effect="non-scaling-stroke"/>
|
||||
</g>
|
||||
<g clip-path="url(#b)" transform="translate(418.40805 346.6008)scale(3.64)">
|
||||
<path d="m255.89 111.218-37.166 21.432-37.165-21.432 37.165-21.463Z" style="opacity:1;fill:#10f48b;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0" transform="translate(-218.72 -111.2)" vector-effect="non-scaling-stroke"/>
|
||||
</g>
|
||||
<g clip-path="url(#c)" transform="translate(322.56685 512.6212)scale(3.64)">
|
||||
<path d="M209.3 153.613v42.927l-37.166-21.463v-42.9z" style="opacity:1;fill:#10f48b;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0" transform="translate(-190.72 -164.36)" vector-effect="non-scaling-stroke"/>
|
||||
</g>
|
||||
<g clip-path="url(#d)" transform="translate(514.24925 512.6212)scale(3.64)">
|
||||
<path d="M273.357 132.181v42.9l-37.166 21.459v-42.927z" style="opacity:1;fill:#10f48b;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0" transform="translate(-254.77 -164.36)" vector-effect="non-scaling-stroke"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -1,29 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg id="logosandtypes_com" data-name="logosandtypes com" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 150 150">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 {
|
||||
fill: #101010;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.cls-3 {
|
||||
fill: url(#linear-gradient);
|
||||
}
|
||||
</style>
|
||||
<linearGradient id="linear-gradient" x1="186.97" y1="96.04" x2="45.7" y2="96.04" gradientTransform="translate(0 150.11) scale(1 -1)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#165cc3"/>
|
||||
<stop offset="1" stop-color="#3ddabb"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g id="Layer_3" data-name="Layer 3">
|
||||
<g id="Layer_2" data-name="Layer 2">
|
||||
<path id="Layer_3-2" data-name="Layer 3-2" class="cls-2" d="M0,0H150V150H0V0Z"/>
|
||||
</g>
|
||||
</g>
|
||||
<path class="cls-1" d="M111.63,75.01c.06,.86,.08,1.72,.08,2.59,0,20.52-16.62,37.16-37.14,37.16-20.52,0-37.16-16.62-37.16-37.14,0-20.52,16.62-37.16,37.14-37.16,0,0,.02,0,.02,0,1.61,0,3.22,.1,4.82,.32l12.7-17.11C62.3,14,30.31,30.3,20.63,60.09c-9.68,29.79,6.62,61.78,36.41,71.47,29.79,9.68,61.78-6.62,71.47-36.41,4.29-13.2,3.59-27.52-1.97-40.24l-14.9,20.11Z"/>
|
||||
<polygon class="cls-3" points="120.26 4.82 74.49 66.53 62.93 53.99 45.67 69.89 76.4 103.32 149.5 4.82 120.26 4.82"/>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 150 150">
|
||||
<defs>
|
||||
<linearGradient id="a" x1="186.97" x2="45.7" y1="96.04" y2="96.04"
|
||||
gradientTransform="matrix(1 0 0 -1 0 150.11)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" stop-color="#165cc3" />
|
||||
<stop offset="1" stop-color="#3ddabb" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path
|
||||
d="M111.63 75.01c.06.86.08 1.72.08 2.59 0 20.52-16.62 37.16-37.14 37.16S37.41 98.14 37.41 77.62s16.62-37.16 37.14-37.16h.02c1.61 0 3.22.1 4.82.32l12.7-17.11C62.3 14 30.31 30.3 20.63 60.09s6.62 61.78 36.41 71.47c29.79 9.68 61.78-6.62 71.47-36.41 4.29-13.2 3.59-27.52-1.97-40.24l-14.9 20.11Z"
|
||||
style="fill:#EFEFEF; mix-blend-mode: difference" />
|
||||
<path d="M120.26 4.82 74.49 66.53 62.93 53.99l-17.26 15.9 30.73 33.43 73.1-98.5z"
|
||||
style="fill:url(#a)" />
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 876 B |
1
auth/assets/custom-icons/icons/cryptee.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg width="1400" height="1400" xmlns="http://www.w3.org/2000/svg"><path d="M699.914 0C1004.659 0 1263.915 194.786 1360 466.662l-399.246-.003C896.674 395.059 803.556 350 699.914 350c-193.276 0-349.957 156.7-349.957 350s156.681 350 349.957 350c103.641 0 196.76-45.059 260.84-116.658L1360 933.34C1263.915 1205.214 1004.659 1400 699.914 1400 313.362 1400 0 1086.6 0 700S313.362 0 699.914 0zm347.087 747.002L1398 747a696.274 696.274 0 0 1-12.453 93H1021a345.75 345.75 0 0 0 26.001-92.998zM1385.547 560A696.301 696.301 0 0 1 1398 653l-351-.002A345.762 345.762 0 0 0 1021 560h364.547z" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 608 B |
3
auth/assets/custom-icons/icons/finary.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg width="500" height="500" viewBox="0 0 500 500" fill="#F1C086" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M166.67 62C74.62 62 0 136.62 0 228.67H333.33C425.38 228.67 500 154.05 500 62H166.67ZM166.67 270.33C74.62 270.33 0 344.95 0 437H154.76C246.81 437 321.43 362.38 321.43 270.33H166.67Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 302 B |
8
auth/assets/custom-icons/icons/girad.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generator: https://ezgif.com/png-to-svg -->
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="195" height="195">
|
||||
<path d="M0,0 L44,0 L70,2 L94,6 L98,8 L98,39 L95,75 L91,98 L85,114 L76,131 L65,145 L58,153 L55,154 L55,144 L51,131 L42,121 L38,118 L38,98 L34,98 L33,29 L28,27 L26,26 L26,89 L22,89 L22,25 L20,25 L20,89 L13,89 L12,29 L11,30 L11,98 L6,98 L6,118 L-3,125 L-9,135 L-11,143 L-11,154 L-15,152 L-25,141 L-35,126 L-43,110 L-48,95 L-52,69 L-54,44 L-55,8 L-50,6 L-26,2 Z " fill="#046097" transform="translate(75,8)"/>
|
||||
<path d="M0,0 L44,0 L70,2 L94,6 L98,8 L98,39 L95,75 L91,98 L85,114 L76,131 L65,145 L58,153 L55,154 L55,144 L51,131 L42,121 L38,118 L38,98 L34,98 L33,29 L28,27 L26,26 L26,89 L22,89 L22,25 L20,24 L22,24 L22,1 L0,1 Z " fill="#0495C0" transform="translate(75,8)"/>
|
||||
<path d="M0,0 L8,0 L13,3 L15,7 L15,15 L6,21 L0,20 L-7,16 L-8,10 L-5,3 Z " fill="#0A669B" transform="translate(93,165)"/>
|
||||
<path d="M0,0 L6,1 L10,5 L11,7 L11,15 L2,21 L-2,20 L0,20 Z " fill="#0D99C1" transform="translate(97,165)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -1,15 +1,7 @@
|
||||
<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g style="mix-blend-mode:difference">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M363.636 23H409.091V477.545H363.636V23ZM0 23H45.4545V477.545H0V23ZM227.273 295.727H181.818V386.636H227.273V295.727ZM272.727 113.909H318.182V204.818H272.727V113.909Z" fill="white"/>
|
||||
</g>
|
||||
<path d="M136.364 386.636H45.4545V477.545H136.364V386.636Z" fill="#EA3326"/>
|
||||
<path d="M500 386.636H409.091V477.545H500V386.636Z" fill="#EA3326"/>
|
||||
<path d="M136.364 295.727H45.4545V386.636H136.364V295.727Z" fill="#EB5829"/>
|
||||
<path d="M318.182 295.727H227.273V386.636H318.182V295.727Z" fill="#EB5829"/>
|
||||
<path d="M500 295.727H409.091V386.636H500V295.727Z" fill="#EB5829"/>
|
||||
<path d="M136.364 23H45.4545V113.909H136.364V23Z" fill="#F7D046"/>
|
||||
<path d="M500 23H409.091V113.909H500V23Z" fill="#F7D046"/>
|
||||
<path d="M227.273 113.909H45.4545V204.818H227.273V113.909Z" fill="#F2A73B"/>
|
||||
<path d="M500 113.909H318.182V204.818H500V113.909Z" fill="#F2A73B"/>
|
||||
<path d="M500 204.818H45.4545V295.727H500V204.818Z" fill="#EE792F"/>
|
||||
</svg>
|
||||
<svg width="500" height="500" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M71.41 73H142.83V143.75H71.41V73ZM357.12 73H428.54V143.75H357.12V73Z" fill="#FFD800"/>
|
||||
<path d="M71.41 143.75H214.25V214.5H71.41V143.75ZM285.7 143.75H428.54V214.5H285.7V143.75Z" fill="#FFAF00"/>
|
||||
<path d="M71.41 214.5H428.54V285.25H71.41V214.5Z" fill="#FF8205"/>
|
||||
<path d="M71.41 285.27H142.83V356.02H71.41V285.27ZM214.27 285.27H285.69V356.02H214.27V285.27ZM357.12 285.27H428.54V356.02H357.12V285.27Z" fill="#FA500F"/>
|
||||
<path d="M0 356.06H214.3V426.82H0V356.06ZM285.7 356.06H500V426.82H285.7V356.06Z" fill="#E10500"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 628 B |
1
auth/assets/custom-icons/icons/oracle_cloud.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg height="2100" viewBox="0 0 32 21" width="3200" xmlns="http://www.w3.org/2000/svg"><path d="m9.9 20.1c-5.5 0-9.9-4.4-9.9-9.9s4.4-9.9 9.9-9.9h11.6c5.5 0 9.9 4.4 9.9 9.9s-4.4 9.9-9.9 9.9zm11.3-3.5c3.6 0 6.4-2.9 6.4-6.4 0-3.6-2.9-6.4-6.4-6.4h-11c-3.6 0-6.4 2.9-6.4 6.4s2.9 6.4 6.4 6.4z" fill="#c74634"/></svg>
|
||||
|
After Width: | Height: | Size: 310 B |
@@ -73,7 +73,10 @@ class AuthenticatorGateway {
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<AuthEntity>> getDiff(int sinceTime, {int limit = 500}) async {
|
||||
Future<(List<AuthEntity>, int?)> getDiff(
|
||||
int sinceTime, {
|
||||
int limit = 500,
|
||||
}) async {
|
||||
try {
|
||||
final response = await _enteDio.get(
|
||||
"/authenticator/entity/diff",
|
||||
@@ -84,11 +87,12 @@ class AuthenticatorGateway {
|
||||
);
|
||||
final List<AuthEntity> authEntities = <AuthEntity>[];
|
||||
final diff = response.data["diff"] as List;
|
||||
final int? unixTimeInMicroSeconds = response.data["timestamp"] as int?;
|
||||
for (var entry in diff) {
|
||||
final AuthEntity entity = AuthEntity.fromMap(entry);
|
||||
authEntities.add(entity);
|
||||
}
|
||||
return authEntities;
|
||||
return (authEntities, unixTimeInMicroSeconds);
|
||||
} catch (e) {
|
||||
if (e is DioException && e.response?.statusCode == 401) {
|
||||
throw UnauthorizedError();
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
"saveAction": "حفظ",
|
||||
"nextTotpTitle": "التالي",
|
||||
"deleteCodeTitle": "حذف الرمز؟",
|
||||
"deleteCodeMessage": "هل أنت متأكد من أنك تريد حذف هذه الشيفرة؟ هذا الإجراء لا رجعة فيه.",
|
||||
"deleteCodeMessage": "هل أنت متيقِّن من أنك تريد حذف هذه الشيفرة؟ هذا الإجراء لا رجعة فيه.",
|
||||
"trashCode": "حذف الكود؟",
|
||||
"trashCodeMessage": "هل أنت متيقِّن أنك تريد حذف الكود الخاص بـ {account}؟",
|
||||
"trash": "سلة المهملات",
|
||||
@@ -513,5 +513,10 @@
|
||||
"free5GB": "5GB مجانًا على <bold-green>ente</bold-green> صور",
|
||||
"loginWithAuthAccount": "سجّل الدخول باستخدام حساب المُصادقة",
|
||||
"freeStorageOffer": "خَصْم 10٪ على صور <bold-green>ente</bold-green>",
|
||||
"freeStorageOfferDescription": "استخدم الكود \"AUTH\" وأحصل على 10٪ خَصْم في السنة الأولى"
|
||||
"freeStorageOfferDescription": "استخدم الكود \"AUTH\" وأحصل على 10٪ خَصْم في السنة الأولى",
|
||||
"advanced": "متقدم",
|
||||
"algorithm": "الخوارزمية",
|
||||
"type": "النوع",
|
||||
"period": "المدّة",
|
||||
"digits": "الأرقام"
|
||||
}
|
||||
@@ -88,6 +88,8 @@
|
||||
"useRecoveryKey": "Použít obnovovací klíč",
|
||||
"incorrectPasswordTitle": "Nesprávné heslo",
|
||||
"welcomeBack": "Vítejte zpět!",
|
||||
"emailAlreadyRegistered": "E-mail je již registrován.",
|
||||
"emailNotRegistered": "E-mail není registrován.",
|
||||
"madeWithLoveAtPrefix": "vyrobeno s ❤️ v ",
|
||||
"supportDevs": "Předplaťte si <bold-green>ente</bold-green>, abyste nás podpořili",
|
||||
"supportDiscount": "Použijte kód \"AUTH\" pro získání 10% slevy na první rok",
|
||||
@@ -495,9 +497,18 @@
|
||||
"appLockOfflineModeWarning": "Zvolili jste si pokračování bez zálohování. Pokud zapomenete heslo do aplikace, přístup k datům bude uzamčen.",
|
||||
"duplicateCodes": "Duplikovat kódy",
|
||||
"noDuplicates": "✨ Žádné duplikáty",
|
||||
"youveNoDuplicateCodesThatCanBeCleared": "Nemáte žádné duplicitní kódy k odstranění",
|
||||
"deduplicateCodes": "Deduplikovat kódy",
|
||||
"deselectAll": "Zrušit výběr všech položek",
|
||||
"selectAll": "Vybrat vše",
|
||||
"deleteDuplicates": "Odstranit duplikáty",
|
||||
"plainHTML": "Prosté HTML"
|
||||
"plainHTML": "Prosté HTML",
|
||||
"tellUsWhatYouThink": "Sdělte nám svůj názor",
|
||||
"dropReviewiOS": "Zanechat recenzi na App Store",
|
||||
"dropReviewAndroid": "Zanechat recenzi na Play Store",
|
||||
"supportEnte": "Podpořte <bold-green>ente</bold-green>",
|
||||
"giveUsAStarOnGithub": "Dejte nám hvězdu na Githubu",
|
||||
"free5GB": "5GB zdarma na <bold-green>ente</bold-green> Fotky",
|
||||
"freeStorageOffer": "10% sleva na <bold-green>ente</bold-green> fotky",
|
||||
"freeStorageOfferDescription": "Použijte kód \"AUTH\" pro získání 10% slevy na první rok"
|
||||
}
|
||||
@@ -91,39 +91,39 @@
|
||||
"emailAlreadyRegistered": "E-Mail ist bereits registriert.",
|
||||
"emailNotRegistered": "E-Mail-Adresse nicht registriert.",
|
||||
"madeWithLoveAtPrefix": "gemacht mit ❤️ bei ",
|
||||
"supportDevs": "Bei <bold-green>ente</bold-green> registrieren, um das Projekt zu unterstützen",
|
||||
"supportDevs": "Abonnieren Sie <bold-green>ente</bold-green>, um das Projekt zu unterstützen",
|
||||
"supportDiscount": "Benutzen Sie den Rabattcode \"AUTH\" für 10 % Rabatt im ersten Jahr",
|
||||
"changeEmail": "E-Mail ändern",
|
||||
"changePassword": "Passwort ändern",
|
||||
"data": "Datei",
|
||||
"data": "Daten",
|
||||
"importCodes": "Codes importieren",
|
||||
"importTypePlainText": "Klartext",
|
||||
"importTypeEnteEncrypted": "Verschlüsselter Ente-Export",
|
||||
"passwordForDecryptingExport": "Passwort um den Export zu entschlüsseln",
|
||||
"passwordEmptyError": "Passwort kann nicht leer sein",
|
||||
"importFromApp": "Importiere Codes von {appName}",
|
||||
"importGoogleAuthGuide": "Exportiere deine Accounts von Google Authenticator zu einem QR-Code, durch die \"Konten übertragen\" Option. Scanne den QR-Code danach mit einem anderen Gerät.\n\nTipp: Du kannst die Kamera eines Laptops verwenden, um ein Foto den dem QR-Code zu erstellen.",
|
||||
"importSelectJsonFile": "Wähle eine JSON-Datei",
|
||||
"importFromApp": "Importieren Sie Codes von {appName}",
|
||||
"importGoogleAuthGuide": "Exportieren Sie Ihre Accounts von Google Authenticator zu einem QR-Code, mithilfe der \"Konten übertragen\" Option. Scanne den QR-Code danach mit einem anderen Gerät.\n\nTipp: Sie können die Kamera eines Laptops verwenden, um ein Foto vom QR-Code zu erstellen.",
|
||||
"importSelectJsonFile": "Wählen Sie eine JSON-Datei",
|
||||
"importSelectAppExport": "{appName} Exportdatei auswählen",
|
||||
"importEnteEncGuide": "Wähle die von Ente exportierte, verschlüsselte JSON-Datei",
|
||||
"importRaivoGuide": "Verwenden Sie die Option \"Export OTPs to Zip archive\" in den Raivo-Einstellungen.\n\nEntpacken Sie die Zip-Datei und importieren Sie die JSON-Datei.",
|
||||
"importEnteEncGuide": "Wählen Sie die von Ente exportierte, verschlüsselte JSON-Datei",
|
||||
"importRaivoGuide": "Verwenden Sie die Option \"Export OTPs to Zip archive\" in den Einstellungen von Raivo.\n\nEntpacken Sie die Zip-Datei und importieren Sie die JSON-Datei.",
|
||||
"importBitwardenGuide": "Verwenden Sie die Option \"Tresor exportieren\" innerhalb der Bitwarden Tools und importieren Sie die unverschlüsselte JSON-Datei.",
|
||||
"importAegisGuide": "Verwenden Sie die Option \"Tresor exportieren\" in den Aegis-Einstellungen.\n\nFalls Ihr Tresor verschlüsselt ist, müssen Sie das Passwort für den Tresor eingeben, um ihn zu entschlüsseln.",
|
||||
"importAegisGuide": "Verwenden Sie die Option \"Tresor exportieren\" in den Einstellungen von Aegis.\n\nFalls Ihr Tresor verschlüsselt ist, müssen Sie das Passwort für den Tresor eingeben, um ihn zu entschlüsseln.",
|
||||
"import2FasGuide": "Verwenden Sie unter \"Einstellungen → Backup\" die Option \"Exportieren\" in 2FAS.\n\nFalls Ihr Backup verschlüsselt ist, müssen Sie das Passwort eingeben, um das Backup zu entschlüsseln.",
|
||||
"importLastpassGuide": "Verwenden Sie die Option \"Konten übertragen → Konten in Datei exportieren\" in den Lastpass Authenticator Einstellungen. \nImportieren Sie anschließend die heruntergeladene JSON-Datei.",
|
||||
"exportCodes": "Codes exportieren",
|
||||
"importLabel": "Importieren",
|
||||
"importInstruction": "Bitte wählen sie eine Datei die Codes in folgendem Format beinhaltet",
|
||||
"importCodeDelimiterInfo": "Codes können in einer neuen Zeile stehen oder durch Kommata getrennt sein",
|
||||
"importInstruction": "Bitte wählen Sie eine Datei die Codes in folgendem Format beinhaltet",
|
||||
"importCodeDelimiterInfo": "Codes können in einer neuen Zeile stehen oder durch ein Komma getrennt sein",
|
||||
"selectFile": "Datei auswählen",
|
||||
"emailVerificationToggle": "E-Mail-Verifizierung",
|
||||
"emailVerificationEnableWarning": "Stellen Sie sicher, eine Kopie Ihrer Zwei-Faktor-Authentifizierung an anderer Stelle zu speichern, um zu vermeiden, dass Sie sich versehentlich aus Ihrem Account aussperren.",
|
||||
"authToChangeEmailVerificationSetting": "Bitte Authentifizieren um die E-Mail Bestätigung zu ändern",
|
||||
"emailVerificationEnableWarning": "Um zu vermeiden, versehentlich aus Ihrem Konto ausgesperrt zu werden, stellen Sie sicher, dass Sie den Zwei-Faktor-Authentifizierungscode für Ihr E-Mail-Konto außerhalb von Ente Auth speichern, bevor Sie die E-Mail-Verifizierung aktivieren.",
|
||||
"authToChangeEmailVerificationSetting": "Bitte authentifizieren, um die E-Mail Bestätigung zu ändern",
|
||||
"authenticateGeneric": "Bitte authentifizieren",
|
||||
"authToViewYourRecoveryKey": "Bitte authentifizieren um ihren Wiederherstellungscode anzuzeigen",
|
||||
"authToChangeYourEmail": "Bitte authentifizieren um ihre Emailadresse zu ändern",
|
||||
"authToChangeYourPassword": "Bitte authentifizieren um ihr Passwort zu ändern",
|
||||
"authToViewSecrets": "Bitte authentifizieren Sie sich, um ihren Wiederherstellungscode anzuzeigen",
|
||||
"authToViewYourRecoveryKey": "Bitte authentifizieren, um Ihren Wiederherstellungscode anzuzeigen",
|
||||
"authToChangeYourEmail": "Bitte authentifizieren, um Ihre E-Mail-Adresse zu ändern",
|
||||
"authToChangeYourPassword": "Bitte authentifizieren, um Ihr Passwort zu ändern",
|
||||
"authToViewSecrets": "Bitte authentifizieren, um Ihren Wiederherstellungscode anzuzeigen",
|
||||
"authToInitiateSignIn": "Bitte authentifizieren, um die Anmeldung zum Backup zu starten.",
|
||||
"ok": "Ok",
|
||||
"cancel": "Abbrechen",
|
||||
@@ -140,18 +140,18 @@
|
||||
"delete": "Löschen",
|
||||
"enterYourPasswordHint": "Geben Sie Ihr Passwort ein",
|
||||
"forgotPassword": "Passwort vergessen",
|
||||
"oops": "Hopla",
|
||||
"oops": "Hoppla",
|
||||
"suggestFeatures": "Features vorschlagen",
|
||||
"faq": "FAQ",
|
||||
"somethingWentWrongMessage": "Ein Fehler ist aufgetreten, bitte versuchen Sie es erneut",
|
||||
"leaveFamily": "Familie verlassen",
|
||||
"leaveFamilyMessage": "Sind Sie sicher, dass Sie den Familien-Plan verlassen wollen?",
|
||||
"inFamilyPlanMessage": "Sie haben einen Familien-Plan!",
|
||||
"hintForMobile": "Lange drücken, um den Code zu bearbeiten oder zu entfernen.",
|
||||
"hintForMobile": "Lange auf einen Code drücken, um ihn zu bearbeiten oder zu entfernen.",
|
||||
"hintForDesktop": "Klicken Sie mit der rechten Maustaste auf einen Code zum Bearbeiten oder Entfernen.",
|
||||
"scan": "Scannen",
|
||||
"scanACode": "Scan einen Code",
|
||||
"verify": "Überprüfen Sie",
|
||||
"verify": "Verifizieren",
|
||||
"verifyEmail": "E-Mail-Adresse verifizieren",
|
||||
"enterCodeHint": "Geben Sie den 6-stelligen Code \naus Ihrer Authentifikator-App ein.",
|
||||
"lostDeviceTitle": "Gerät verloren?",
|
||||
@@ -172,9 +172,10 @@
|
||||
},
|
||||
"invalidQRCode": "Ungültiger QR-Code",
|
||||
"noRecoveryKeyTitle": "Kein Wiederherstellungsschlüssel?",
|
||||
"enterEmailHint": "Geben Sie Ihre E-Mail Adresse ein",
|
||||
"invalidEmailTitle": "Ungültige E-Mail Adresse",
|
||||
"invalidEmailMessage": "Bitte geben Sie eine gültige E-Mail Adresse ein.",
|
||||
"enterEmailHint": "Geben Sie Ihre E-Mail-Adresse ein",
|
||||
"enterNewEmailHint": "Gib deine neue E-Mail-Adresse ein",
|
||||
"invalidEmailTitle": "Ungültige E-Mail-Adresse",
|
||||
"invalidEmailMessage": "Bitte geben Sie eine gültige E-Mail-Adresse ein.",
|
||||
"deleteAccount": "Konto löschen",
|
||||
"deleteAccountQuery": "Es tut uns leid, dass Sie gehen. Haben Sie ein Problem?",
|
||||
"yesSendFeedbackAction": "Ja, Feedback senden",
|
||||
@@ -187,7 +188,7 @@
|
||||
"moderateStrength": "Mittel",
|
||||
"confirmPassword": "Bestätigen Sie das Passwort",
|
||||
"close": "Schließen",
|
||||
"oopsSomethingWentWrong": "Ups, da ist etwas schief gelaufen.",
|
||||
"oopsSomethingWentWrong": "Hoppla, da ist etwas schiefgelaufen.",
|
||||
"selectLanguage": "Sprache auswählen",
|
||||
"language": "Sprache",
|
||||
"social": "Social",
|
||||
@@ -203,26 +204,26 @@
|
||||
"noResult": "Kein Ergebnis",
|
||||
"addCode": "Code hinzufügen",
|
||||
"scanAQrCode": "QR-Code scannen",
|
||||
"enterDetailsManually": "Details manuell hinzufügen",
|
||||
"enterDetailsManually": "Daten manuell hinzufügen",
|
||||
"edit": "Editieren",
|
||||
"share": "Teilen",
|
||||
"shareCodes": "Codes teilen",
|
||||
"shareCodesDuration": "Wählen Sie die Dauer aus, für die Sie die Codes teilen möchten.",
|
||||
"restore": "Wiederherstellen",
|
||||
"copiedToClipboard": "In die Zwischenablage kopieren",
|
||||
"copiedNextToClipboard": "Nächster Code wurde in die Zwischenablage kopiert",
|
||||
"copiedToClipboard": "In die Zwischenablage kopiert",
|
||||
"copiedNextToClipboard": "Nächster Code in die Zwischenablage kopiert",
|
||||
"error": "Fehler",
|
||||
"recoveryKeyCopiedToClipboard": "Wiederherstellungsschlüssel in die Zwischenablage kopiert",
|
||||
"recoveryKeyOnForgotPassword": "Sollten sie ihr Passwort vergessen, dann ist dieser Schlüssel die einzige Möglichkeit ihre Daten wiederherzustellen.",
|
||||
"recoveryKeySaveDescription": "Wir speichern diesen Schlüssel nicht. Sichern sie dieses diesen Schlüssel bestehend aus 24 Wörtern an einem sicheren Platz.",
|
||||
"recoveryKeySaveDescription": "Wir speichern diesen Schlüssel nicht. Sichern Sie diesen Schlüssel bestehend aus 24 Wörtern an einem sicheren Platz.",
|
||||
"doThisLater": "Auf später verschieben",
|
||||
"saveKey": "Schlüssel speichern",
|
||||
"save": "Speichern",
|
||||
"send": "Senden",
|
||||
"saveOrSendDescription": "Möchtest du dies in deinem Speicher (standardmäßig im Ordner Downloads) oder an andere Apps senden?",
|
||||
"saveOrSendDescription": "Möchtest du dies in deinem Speicher (standardmäßig im Ordner Downloads) speichern oder an andere Apps senden?",
|
||||
"saveOnlyDescription": "Möchtest du dies in deinem Speicher (standardmäßig im Ordner Downloads) speichern?",
|
||||
"back": "Zurück",
|
||||
"createAccount": "Account erstellen",
|
||||
"createAccount": "Konto erstellen",
|
||||
"passwordStrength": "Passwortstärke: {passwordStrengthValue}",
|
||||
"@passwordStrength": {
|
||||
"description": "Text to indicate the password strength",
|
||||
@@ -244,17 +245,17 @@
|
||||
"changePasswordTitle": "Passwort ändern",
|
||||
"resetPasswordTitle": "Passwort zurücksetzen",
|
||||
"encryptionKeys": "Verschlüsselungsschlüssel",
|
||||
"passwordWarning": "Wir speichern dieses Passwort nicht. Wenn du es vergisst, <underline>können wir deine Daten nicht entschlüsseln</underline>",
|
||||
"enterPasswordToEncrypt": "Gib ein Passwort ein, mit dem wir deine Daten verschlüsseln können",
|
||||
"enterNewPasswordToEncrypt": "Gib ein neues Passwort ein, mit dem wir deine Daten verschlüsseln können",
|
||||
"passwordWarning": "Wir speichern dieses Passwort nicht. Wenn Sie es vergessen, <underline>können wir Ihre Daten nicht entschlüsseln</underline>",
|
||||
"enterPasswordToEncrypt": "Geben Sie ein Passwort ein, mit dem wir Ihre Daten verschlüsseln können",
|
||||
"enterNewPasswordToEncrypt": "Geben Sie ein neues Passwort ein, mit dem wir Ihre Daten verschlüsseln können",
|
||||
"passwordChangedSuccessfully": "Passwort erfolgreich geändert",
|
||||
"generatingEncryptionKeys": "Generierung von Verschlüsselungsschlüsseln...",
|
||||
"continueLabel": "Weiter",
|
||||
"insecureDevice": "Unsicheres Gerät",
|
||||
"sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Es tut uns leid, wir konnten keine sicheren Schlüssel auf diesem Gerät generieren.\n\nBitte registrieren Sie sich auf einem anderen Gerät.",
|
||||
"howItWorks": "So funktioniert's",
|
||||
"ackPasswordLostWarning": "Ich verstehe, dass der Verlust meines Passworts zum Verlust meiner Daten führen kann, denn diese ist <underline>Ende-zu-Ende verschlüsselt</underline>.",
|
||||
"loginTerms": "Durch das Klicken auf den Login-Button, stimme ich <u-terms>den Nutzungsbedingungen</u-terms> und den <u-policy>Datenschutzbestimmungen</u-policy> zu",
|
||||
"ackPasswordLostWarning": "Ich verstehe, dass der Verlust meines Passworts zum Verlust meiner Daten führen kann, denn diese sind <underline>Ende-zu-Ende verschlüsselt</underline>.",
|
||||
"loginTerms": "Durch das Klicken auf den Login-Button, stimme ich den <u-terms> Nutzungsbedingungen</u-terms> und den <u-policy>Datenschutzbestimmungen</u-policy> zu",
|
||||
"logInLabel": "Einloggen",
|
||||
"logout": "Ausloggen",
|
||||
"areYouSureYouWantToLogout": "Sind sie sicher, dass sie sich ausloggen möchten?",
|
||||
@@ -266,7 +267,7 @@
|
||||
"systemTheme": "System",
|
||||
"verifyingRecoveryKey": "Verifiziere Wiederherstellungsschlüssel...",
|
||||
"recoveryKeyVerified": "Wiederherstellungsschlüssel verifiziert",
|
||||
"recoveryKeySuccessBody": "Großartig! Ihr Wiederherstellungsschlüssel ist gültig. Vielen Dank für die Verifizierung.\n\nBitte denken sie daran, dass sie ihren Wiederherstellungsschlüssel sicher aufbewahren.",
|
||||
"recoveryKeySuccessBody": "Großartig! Ihr Wiederherstellungsschlüssel ist gültig. Vielen Dank für die Verifizierung.\n\nBitte denken Sie daran, den Wiederherstellungsschlüssel sicher aufzubewahren.",
|
||||
"invalidRecoveryKey": "Der eingegebene Wiederherstellungsschlüssel ist nicht gültig. Bitte stellen sie sicher, dass er aus 24 Wörtern besteht und prüfen sie die Schreibweise eines jeden.\n\nSollten sie einen Wiederherstellungsschlüssel im alten Format eingegeben haben vergewissern sie sich, dass er 64 Zeichen lang ist und prüfen sie jedes dieser Zeichen.",
|
||||
"recreatePasswordTitle": "Neues Passwort erstellen",
|
||||
"recreatePasswordBody": "Das benutzte Gerät ist nicht leistungsfähig genug das Passwort zu prüfen. Wir können es aber neu erstellen damit es auf jedem Gerät funktioniert. \n\nBitte loggen sie sich mit ihrem Wiederherstellungsschlüssel ein und erstellen sie ein neues Passwort (Sie können das selbe Passwort wieder verwenden, wenn sie möchten).",
|
||||
@@ -277,15 +278,15 @@
|
||||
"recoveryKeyVerifyReason": "Ihr Wiederherstellungsschlüssel ist der einzige Weg ihre Fotos wiederherzustellen sollten, sie ihr Passwort vergessen. Sie finden ihren Wiederherstellungsschlüssel unter Einstellungen > Account.\n\nBitte tragen sie ihren Wiederherstellungsschlüssel hier ein um zu prüfen ob sie in korrekt abgespeichert haben.",
|
||||
"confirmYourRecoveryKey": "Wiederherstellungsschlüssel bestätigen",
|
||||
"confirm": "Bestätigen",
|
||||
"emailYourLogs": "Email mit Logs senden",
|
||||
"emailYourLogs": "E-Mail mit Logs senden",
|
||||
"pleaseSendTheLogsTo": "Bitte Logs an {toEmail} senden",
|
||||
"copyEmailAddress": "Emailadresse kopieren",
|
||||
"copyEmailAddress": "E-Mail-Adresse kopieren",
|
||||
"exportLogs": "Logs exportieren",
|
||||
"enterYourRecoveryKey": "Wiederherstellungsschlüssel eingeben",
|
||||
"tempErrorContactSupportIfPersists": "Etwas ist schiefgelaufen. Bitte versuchen sie es später noch einmal. Sollte der Fehler weiter bestehen, kontaktieren sie unser Supportteam.",
|
||||
"tempErrorContactSupportIfPersists": "Etwas ist schiefgelaufen. Bitte versuchen Sie es später noch einmal. Sollte der Fehler weiter bestehen, kontaktieren sie unser Supportteam.",
|
||||
"networkHostLookUpErr": "Ente ist im Moment nicht erreichbar. Bitte überprüfen Sie Ihre Netzwerkeinstellungen. Sollte das Problem bestehen bleiben, wenden Sie sich bitte an den Support.",
|
||||
"networkConnectionRefusedErr": "Ente ist im Moment nicht erreichbar. Bitte versuchen Sie es später erneut. Sollte das Problem bestehen bleiben, wenden Sie sich bitte an den Support.",
|
||||
"itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Etwas ist schiefgelaufen. Bitte versuchen sie es später noch einmal. Sollte der Fehler weiter bestehen, kontaktieren sie unser Supportteam.",
|
||||
"itLooksLikeSomethingWentWrongPleaseRetryAfterSome": "Etwas ist schiefgelaufen. Bitte versuchen Sie es später noch einmal. Sollte der Fehler weiter bestehen, kontaktieren Sie unser Supportteam.",
|
||||
"about": "Über uns",
|
||||
"weAreOpenSource": "Wir sind Opensource!",
|
||||
"privacy": "Datenschutz",
|
||||
@@ -316,10 +317,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"sorry": "Entschuldigen sie",
|
||||
"importFailureDesc": "Ausgewählte Datei ließ sich nicht verarbeiten.\nBitte wenden sie sich an support@ente.io für Hilfe!",
|
||||
"sorry": "Entschuldigen Sie",
|
||||
"importFailureDesc": "Ausgewählte Datei ließ sich nicht verarbeiten.\nBitte wenden Sie sich an support@ente.io für Hilfe!",
|
||||
"pendingSyncs": "Warnung",
|
||||
"pendingSyncsWarningBody": "Einige Codes wurden nicht gesichert.\n\nBitte gehen sie sicher, dass sie einen Backupcode für diese Codes haben bevor sie sich ausloggen.",
|
||||
"pendingSyncsWarningBody": "Einige Codes wurden nicht gesichert.\n\nBitte gehen Sie sicher, dass Sie einen Backupcode für diese Codes haben bevor Sie sich ausloggen.",
|
||||
"checkInboxAndSpamFolder": "Bitte überprüfe deinen E-Mail-Posteingang (und Spam), um die Verifizierung abzuschließen",
|
||||
"tapToEnterCode": "Antippen, um den Code einzugeben",
|
||||
"resendEmail": "E-Mail erneut senden",
|
||||
@@ -339,10 +340,10 @@
|
||||
"mostFrequentlyUsed": "Häufig verwendet",
|
||||
"mostRecentlyUsed": "Zuletzt verwendet",
|
||||
"activeSessions": "Aktive Sitzungen",
|
||||
"somethingWentWrongPleaseTryAgain": "Ein Fehler ist aufgetreten, bitte versuche es erneut",
|
||||
"thisWillLogYouOutOfThisDevice": "Dadurch wirst du von diesem Gerät abgemeldet!",
|
||||
"thisWillLogYouOutOfTheFollowingDevice": "Dadurch wirst du von folgendem Gerät abgemeldet:",
|
||||
"terminateSession": "Sitzungen beenden?",
|
||||
"somethingWentWrongPleaseTryAgain": "Ein Fehler ist aufgetreten, bitte erneut versuchen",
|
||||
"thisWillLogYouOutOfThisDevice": "Dadurch werden Sie von diesem Gerät abgemeldet!",
|
||||
"thisWillLogYouOutOfTheFollowingDevice": "Dadurch werden Sie vom folgendem Gerät abgemeldet:",
|
||||
"terminateSession": "Sitzung beenden?",
|
||||
"terminate": "Beenden",
|
||||
"thisDevice": "Dieses Gerät",
|
||||
"toResetVerifyEmail": "Um Ihr Passwort zurückzusetzen, verifizieren Sie bitte zuerst Ihre E-Mail-Adresse.",
|
||||
@@ -352,7 +353,7 @@
|
||||
"incorrectCode": "Falscher Code",
|
||||
"sorryTheCodeYouveEnteredIsIncorrect": "Leider ist der eingegebene Code falsch",
|
||||
"emailChangedTo": "E-Mail-Adresse geändert zu {newEmail}",
|
||||
"authenticationFailedPleaseTryAgain": "Authentifizierung fehlgeschlagen, versuchen Sie es bitte erneut",
|
||||
"authenticationFailedPleaseTryAgain": "Authentifizierung fehlgeschlagen, bitte erneut versuchen",
|
||||
"authenticationSuccessful": "Authentifizierung erfolgreich!",
|
||||
"twofactorAuthenticationSuccessfullyReset": "Zwei-Faktor-Authentifizierung (2FA) erfolgreich zurückgesetzt",
|
||||
"incorrectRecoveryKey": "Falscher Wiederherstellungs-Schlüssel",
|
||||
@@ -365,22 +366,22 @@
|
||||
"passwordToEncryptExport": "Passwort zum Verschlüssen des Exports",
|
||||
"export": "Export",
|
||||
"useOffline": "Ohne Backup verwenden",
|
||||
"signInToBackup": "Melde dich an, um deine Codes zu sichern",
|
||||
"signInToBackup": "Melden Sie sich an, um Ihre Codes zu sichern",
|
||||
"singIn": "Anmelden",
|
||||
"sigInBackupReminder": "Bitte exportieren Sie Ihre Codes, um sicherzustellen, dass Sie ein Backup haben, aus dem Sie wiederherstellen können.",
|
||||
"sigInBackupReminder": "Bitte exportieren Sie Ihre Codes, um sicherzustellen, dass Sie ein Backup haben, das Sie wiederherstellen können.",
|
||||
"offlineModeWarning": "Sie haben sich dafür entschieden, ohne Sicherungen fortzufahren. Bitte führen Sie manuelle Sicherungen durch, um sicherzustellen, dass Ihre Codes sicher sind.",
|
||||
"showLargeIcons": "Große Symbole anzeigen",
|
||||
"compactMode": "Kompaktmodus",
|
||||
"shouldHideCode": "Codes ausblenden",
|
||||
"doubleTapToViewHiddenCode": "Sie können auf einen Eintrag doppelt tippen, um den Code anzuzeigen",
|
||||
"focusOnSearchBar": "Suche bei App-Start automatisch öffnen",
|
||||
"confirmUpdatingkey": "Sind Sie sich sicher, dass Sie den Secret Key bearbeiten wollen?",
|
||||
"focusOnSearchBar": "Suche beim App-Start fokussieren",
|
||||
"confirmUpdatingkey": "Sind Sie sich sicher, dass Sie den geheimen Schlüssel bearbeiten wollen?",
|
||||
"minimizeAppOnCopy": "Beim Kopieren App minimieren",
|
||||
"editCodeAuthMessage": "Authentifizieren, um Code zu bearbeiten",
|
||||
"deleteCodeAuthMessage": "Authentifizieren, um Code zu löschen",
|
||||
"showQRAuthMessage": "Authentifizieren, um QR-Code anzuzeigen",
|
||||
"confirmAccountDeleteTitle": "Kontolöschung bestätigen",
|
||||
"confirmAccountDeleteMessage": "Dieses Konto ist mit anderen Ente-Apps verknüpft, falls du welche verwendest.\n\nDeine hochgeladenen Daten werden in allen Ente-Apps zur Löschung vorgemerkt und dein Konto wird endgültig gelöscht.",
|
||||
"confirmAccountDeleteMessage": "Dieses Konto ist mit anderen Ente-Apps verknüpft, falls Sie welche verwenden.\n\nIhre hochgeladenen Daten werden in allen Ente-Apps zur Löschung vorgemerkt und Ihr Konto wird endgültig gelöscht.",
|
||||
"androidBiometricHint": "Identität bestätigen",
|
||||
"@androidBiometricHint": {
|
||||
"description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters."
|
||||
@@ -417,7 +418,7 @@
|
||||
"@goToSettings": {
|
||||
"description": "Message showed on a button that the user can click to go to settings pages from the current dialog. It is used on both Android and iOS side. Maximum 30 characters."
|
||||
},
|
||||
"androidGoToSettingsDescription": "Auf Ihrem Gerät ist keine biometrische Authentifizierung eingerichtet. Gehen Sie „Einstellungen“ > „Sicherheit“, um die biometrische Authentifizierung hinzuzufügen.",
|
||||
"androidGoToSettingsDescription": "Auf Ihrem Gerät ist keine biometrische Authentifizierung eingerichtet. Gehen Sie zu 'Einstellungen > Sicherheit', um die biometrische Authentifizierung hinzuzufügen.",
|
||||
"@androidGoToSettingsDescription": {
|
||||
"description": "Message advising the user to go to the settings and configure biometric on their device. It shows in a dialog on Android side."
|
||||
},
|
||||
@@ -434,9 +435,9 @@
|
||||
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on iOS side. Maximum 30 characters."
|
||||
},
|
||||
"noInternetConnection": "Keine Internetverbindung",
|
||||
"pleaseCheckYourInternetConnectionAndTryAgain": "Bitte überprüfe deine Internetverbindung und versuche es erneut.",
|
||||
"pleaseCheckYourInternetConnectionAndTryAgain": "Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie erneut.",
|
||||
"signOutFromOtherDevices": "Von anderen Geräten abmelden",
|
||||
"signOutOtherBody": "Falls du denkst, dass jemand dein Passwort kennen könnte, kannst du alle anderen Geräte von deinem Account abmelden.",
|
||||
"signOutOtherBody": "Falls Sie denken, dass jemand Ihr Passwort kennen könnte, können Sie alle anderen Geräte forcieren, sich von Ihrem Konto abzumelden.",
|
||||
"signOutOtherDevices": "Andere Geräte abmelden",
|
||||
"doNotSignOut": "Nicht abmelden",
|
||||
"hearUsWhereTitle": "Wie hast du von Ente erfahren? (optional)",
|
||||
@@ -458,7 +459,7 @@
|
||||
"pinText": "Anpinnen",
|
||||
"unpinText": "Lösen",
|
||||
"pinnedCodeMessage": "{code} wurde angepinnt",
|
||||
"unpinnedCodeMessage": "{code} wird nicht weiter angepinnt",
|
||||
"unpinnedCodeMessage": "{code} wurde losgelöst",
|
||||
"pinned": "Angeheftet",
|
||||
"tags": "Tags",
|
||||
"createNewTag": "Neuen Tag erstellen",
|
||||
@@ -474,7 +475,7 @@
|
||||
"rawCodeData": "Rohcode Daten",
|
||||
"appLock": "App-Sperre",
|
||||
"noSystemLockFound": "Keine Systemsperre gefunden",
|
||||
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "Um die App-Sperre zu aktivieren, konfiguriere bitte den Gerätepasscode oder die Bildschirmsperre in den Systemeinstellungen.",
|
||||
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen": "Um die App-Sperre zu aktivieren, konfigurieren Sie bitte den Gerätepasscode oder die Bildschirmsperre in den Systemeinstellungen.",
|
||||
"autoLock": "Automatisches Sperren",
|
||||
"immediately": "Sofort",
|
||||
"reEnterPassword": "Passwort erneut eingeben",
|
||||
@@ -494,23 +495,29 @@
|
||||
"setNewPin": "Neue PIN festlegen",
|
||||
"importFailureDescNew": "Die ausgewählte Datei konnte nicht verarbeitet werden.",
|
||||
"appLockNotEnabled": "App-Sperre nicht aktiviert",
|
||||
"appLockNotEnabledDescription": "Bitte aktivieren Sie die App-Sperre über Security > App-Sperre",
|
||||
"authToViewPasskey": "Bitte authentifizieren, um deinen Passkey zu sehen",
|
||||
"duplicateCodes": "Doppelte Codes",
|
||||
"appLockNotEnabledDescription": "Bitte aktivieren Sie die App-Sperre über Sicherheit > App-Sperre",
|
||||
"authToViewPasskey": "Bitte authentifizieren, um Ihren Passkey zu sehen",
|
||||
"appLockOfflineModeWarning": "Sie haben sich dazu entschieden, ohne Sicherungen fortzufahren. Wenn Sie Ihre App-Sperre vergessen, können Sie nicht mehr auf Ihre Daten zugreifen.",
|
||||
"duplicateCodes": "Codes duplizieren",
|
||||
"noDuplicates": "✨ Keine Duplikate",
|
||||
"youveNoDuplicateCodesThatCanBeCleared": "Du hast keine doppelten Codes, die bereinigt werden können",
|
||||
"deduplicateCodes": "Codes deduplizieren",
|
||||
"deselectAll": "Alle abwählen",
|
||||
"selectAll": "Alles auswählen",
|
||||
"selectAll": "Alle auswählen",
|
||||
"deleteDuplicates": "Duplikate löschen",
|
||||
"plainHTML": "Reines HTML",
|
||||
"tellUsWhatYouThink": "Sagen Sie uns, was Sie denken",
|
||||
"dropReviewiOS": "Hinterlasse eine Rezension im App Store",
|
||||
"dropReviewAndroid": "Hinterlasse eine Rezension im Google Play Store",
|
||||
"supportEnte": "Support <bold-green>ente</bold-green>",
|
||||
"dropReviewiOS": "Hinterlassen Sie eine Rezension im App Store",
|
||||
"dropReviewAndroid": "Hinterlassen Sie eine Rezension im Google Play Store",
|
||||
"supportEnte": "Unterstütze <bold-green>ente</bold-green>",
|
||||
"giveUsAStarOnGithub": "Gib uns einen Stern auf Github",
|
||||
"free5GB": "5GB kostenlos auf <bold-green>ente</bold-green> Photos",
|
||||
"loginWithAuthAccount": "Mit Ihrem Auth Account anmelden",
|
||||
"freeStorageOffer": "10% Rabatt für <bold-green>ente</bold-green> Photos",
|
||||
"freeStorageOfferDescription": "Verwende den Code \"AUTH\", um 10% im 1. Jahr zu sparen"
|
||||
"freeStorageOfferDescription": "Verwende den Code \"AUTH\", um 10% im ersten Jahr zu sparen",
|
||||
"advanced": "Erweitert",
|
||||
"algorithm": "Algorithmus",
|
||||
"type": "Typ",
|
||||
"period": "Periode",
|
||||
"digits": "Ziffern"
|
||||
}
|
||||
@@ -173,6 +173,7 @@
|
||||
"invalidQRCode": "Invalid QR code",
|
||||
"noRecoveryKeyTitle": "No recovery key?",
|
||||
"enterEmailHint": "Enter your email address",
|
||||
"enterNewEmailHint": "Enter your new email address",
|
||||
"invalidEmailTitle": "Invalid email address",
|
||||
"invalidEmailMessage": "Please enter a valid email address.",
|
||||
"deleteAccount": "Delete account",
|
||||
@@ -513,5 +514,10 @@
|
||||
"free5GB": "5GB free on <bold-green>ente</bold-green> Photos",
|
||||
"loginWithAuthAccount": "Login with your Auth account",
|
||||
"freeStorageOffer": "10% off on <bold-green>ente</bold-green> photos",
|
||||
"freeStorageOfferDescription": "Use code \"AUTH\" to get 10% off first year"
|
||||
"freeStorageOfferDescription": "Use code \"AUTH\" to get 10% off first year",
|
||||
"advanced": "Advanced",
|
||||
"algorithm": "Algorithm",
|
||||
"type": "Type",
|
||||
"period": "Period",
|
||||
"digits": "Digits"
|
||||
}
|
||||
@@ -173,6 +173,7 @@
|
||||
"invalidQRCode": "QR code non valide",
|
||||
"noRecoveryKeyTitle": "Pas de clé de récupération ?",
|
||||
"enterEmailHint": "Entrez votre adresse e-mail",
|
||||
"enterNewEmailHint": "Saisissez votre nouvelle adresse email",
|
||||
"invalidEmailTitle": "Adresse e-mail invalide",
|
||||
"invalidEmailMessage": "Veuillez saisir une adresse e-mail valide.",
|
||||
"deleteAccount": "Supprimer le compte",
|
||||
@@ -513,5 +514,10 @@
|
||||
"free5GB": "5 Go gratuits sur <bold-green>Ente</bold-green> Photos",
|
||||
"loginWithAuthAccount": "Connectez-vous avec votre compte Auth",
|
||||
"freeStorageOffer": "10% de réduction sur <bold-green>Ente</bold-green> Photos",
|
||||
"freeStorageOfferDescription": "Utilisez le code coupon \"AUTH\" pour obtenir 10% de réduction la première année"
|
||||
"freeStorageOfferDescription": "Utilisez le code coupon \"AUTH\" pour obtenir 10% de réduction la première année",
|
||||
"advanced": "Avancé",
|
||||
"algorithm": "Algorithme",
|
||||
"type": "Type",
|
||||
"period": "Période",
|
||||
"digits": "Chiffres"
|
||||
}
|
||||
@@ -173,6 +173,7 @@
|
||||
"invalidQRCode": "Érvénytelen QR-kód",
|
||||
"noRecoveryKeyTitle": "Nincs helyreállítási kulcs?",
|
||||
"enterEmailHint": "Adja meg az e-mail címét",
|
||||
"enterNewEmailHint": "Add meg az új e-mail címed",
|
||||
"invalidEmailTitle": "Érvénytelen e-mail cím",
|
||||
"invalidEmailMessage": "Kérjük, adjon meg egy érvényes e-mail címet.",
|
||||
"deleteAccount": "Fiók törlése",
|
||||
@@ -513,5 +514,6 @@
|
||||
"free5GB": "5GB ingyen <bold-green>ente <bold-green> Photos",
|
||||
"loginWithAuthAccount": "Jelentkezzen be Auth fiókjával",
|
||||
"freeStorageOffer": "10% kedvezmény on <bold-green>ente<bold-green> photos",
|
||||
"freeStorageOfferDescription": "Használja az \"AUTH\" kódot, hogy 10% kedvezményt kapjon az első évben"
|
||||
"freeStorageOfferDescription": "Használja az \"AUTH\" kódot, hogy 10% kedvezményt kapjon az első évben",
|
||||
"type": "Típus"
|
||||
}
|
||||
@@ -173,6 +173,7 @@
|
||||
"invalidQRCode": "Codice QR non valido",
|
||||
"noRecoveryKeyTitle": "Nessuna chiave di recupero?",
|
||||
"enterEmailHint": "Inserisci il tuo indirizzo email",
|
||||
"enterNewEmailHint": "Inserisci il tuo nuovo indirizzo email",
|
||||
"invalidEmailTitle": "Indirizzo email non valido",
|
||||
"invalidEmailMessage": "Inserisci un indirizzo email valido.",
|
||||
"deleteAccount": "Elimina account",
|
||||
@@ -513,5 +514,10 @@
|
||||
"free5GB": "5GB gratis su <bold-green>ente</bold-green> Foto",
|
||||
"loginWithAuthAccount": "Accedi con il tuo account Auth",
|
||||
"freeStorageOffer": "10% di sconto su <bold-green>ente</bold-green> Foto",
|
||||
"freeStorageOfferDescription": "Utilizzare il codice \"AUTH\" per ottenere il 10% di sconto al primo anno"
|
||||
"freeStorageOfferDescription": "Utilizzare il codice \"AUTH\" per ottenere il 10% di sconto al primo anno",
|
||||
"advanced": "Avanzate",
|
||||
"algorithm": "Algoritmo",
|
||||
"type": "Tipo",
|
||||
"period": "Periodo",
|
||||
"digits": "Cifre"
|
||||
}
|
||||
@@ -508,9 +508,15 @@
|
||||
"tellUsWhatYouThink": "Pasakykite mums, ką manote",
|
||||
"dropReviewiOS": "Rašyti apžvalgą parduotuvėje „App Store“",
|
||||
"dropReviewAndroid": "Rašyti apžvalgą parduotuvėje „Play“ parduotuvė“",
|
||||
"supportEnte": "Paremti „<bold-green>ente</bold-green>“",
|
||||
"giveUsAStarOnGithub": "Suteikite mums žvaigždutę platformoje „Github“",
|
||||
"free5GB": "5 GB nemokami programai „<bold-green>ente</bold-green>“ nuotraukos",
|
||||
"loginWithAuthAccount": "Prisijungti su jūsų „Auth“ paskyra",
|
||||
"freeStorageOffer": "10 % nuolaida programai „<bold-green>ente</bold-green>“ nuotraukos",
|
||||
"freeStorageOfferDescription": "Naudokite kodą „AUTH“, kad gautumėte 10 % nuolaida pirmiesiems metams. "
|
||||
"freeStorageOfferDescription": "Naudokite kodą „AUTH“, kad gautumėte 10 % nuolaida pirmiesiems metams. ",
|
||||
"advanced": "Išplėstiniai",
|
||||
"algorithm": "Algoritmas",
|
||||
"type": "Tipas",
|
||||
"period": "Laikotarpis",
|
||||
"digits": "Skaitmenys"
|
||||
}
|
||||
@@ -173,6 +173,7 @@
|
||||
"invalidQRCode": "Ongeldige QR-code",
|
||||
"noRecoveryKeyTitle": "Geen herstelsleutel?",
|
||||
"enterEmailHint": "Voer je e-mailadres in",
|
||||
"enterNewEmailHint": "Voer uw nieuwe e-mailadres in",
|
||||
"invalidEmailTitle": "Ongeldig e-mailadres",
|
||||
"invalidEmailMessage": "Voer een geldig e-mailadres in.",
|
||||
"deleteAccount": "Account verwijderen",
|
||||
@@ -513,5 +514,10 @@
|
||||
"free5GB": "5GB gratis op <bold-green>ente</bold-green> Photos",
|
||||
"loginWithAuthAccount": "Log in met je Auth account",
|
||||
"freeStorageOffer": "10% korting op <bold-green>ente</bold-green> photos",
|
||||
"freeStorageOfferDescription": "Gebruik de code \"AUTH\" voor 10% korting op je eerste jaar"
|
||||
"freeStorageOfferDescription": "Gebruik de code \"AUTH\" voor 10% korting op je eerste jaar",
|
||||
"advanced": "Geavanceerd",
|
||||
"algorithm": "Algoritme",
|
||||
"type": "Type",
|
||||
"period": "Periode",
|
||||
"digits": "Cijfers"
|
||||
}
|
||||
@@ -173,6 +173,7 @@
|
||||
"invalidQRCode": "Nieprawidłowy kod QR",
|
||||
"noRecoveryKeyTitle": "Brak klucza odzyskiwania?",
|
||||
"enterEmailHint": "Wprowadź adres e-mail",
|
||||
"enterNewEmailHint": "Wprowadź nowy adres e-mail",
|
||||
"invalidEmailTitle": "Nieprawidłowy adres e-mail",
|
||||
"invalidEmailMessage": "Prosimy podać prawidłowy adres e-mail.",
|
||||
"deleteAccount": "Usuń konto",
|
||||
@@ -513,5 +514,10 @@
|
||||
"free5GB": "5 GB za darmo na zdjęcia <bold-green>ente</bold-green>",
|
||||
"loginWithAuthAccount": "Zaloguj się przy użyciu konta Auth",
|
||||
"freeStorageOffer": "10% zniżki na zdjęcia <bold-green>ente</bold-green>",
|
||||
"freeStorageOfferDescription": "Użyj kodu „AUTH”, aby uzyskać 10% zniżki na pierwszy rok"
|
||||
"freeStorageOfferDescription": "Użyj kodu „AUTH”, aby uzyskać 10% zniżki na pierwszy rok",
|
||||
"advanced": "Zaawansowane",
|
||||
"algorithm": "Algorytm",
|
||||
"type": "Rodzaj",
|
||||
"period": "Okres",
|
||||
"digits": "Cyfry"
|
||||
}
|
||||
@@ -173,6 +173,7 @@
|
||||
"invalidQRCode": "QR Code inválido",
|
||||
"noRecoveryKeyTitle": "Sem chave de recuperação?",
|
||||
"enterEmailHint": "Insira o endereço de e-mail",
|
||||
"enterNewEmailHint": "Insira seu novo e-mail",
|
||||
"invalidEmailTitle": "Endereço de e-mail inválido",
|
||||
"invalidEmailMessage": "Insira um endereço de e-mail válido.",
|
||||
"deleteAccount": "Excluir conta",
|
||||
@@ -513,5 +514,10 @@
|
||||
"free5GB": "5GB grátis no <bold-green>ente</bold-green> Photos",
|
||||
"loginWithAuthAccount": "Registrar-se com sua conta Auth",
|
||||
"freeStorageOffer": "10% de desconto no <bold-green>ente</bold-green> photos",
|
||||
"freeStorageOfferDescription": "Use o cupom \"AUTH\" para obter 10% de desconto no primeiro ano"
|
||||
"freeStorageOfferDescription": "Use o cupom \"AUTH\" para obter 10% de desconto no primeiro ano",
|
||||
"advanced": "Avançado",
|
||||
"algorithm": "Algoritmo",
|
||||
"type": "Tipo",
|
||||
"period": "Período",
|
||||
"digits": "Dígitos"
|
||||
}
|
||||
@@ -6,10 +6,10 @@
|
||||
"@counterAppBarTitle": {
|
||||
"description": "Text shown in the AppBar of the Counter Page"
|
||||
},
|
||||
"onBoardingBody": "2 Faktörlü Kimlik Doğrulama kodlarınızı koruyun",
|
||||
"onBoardingBody": "2FA kodlarınızı güvenli bir şekilde yedekleyin",
|
||||
"onBoardingGetStarted": "Başlayın",
|
||||
"setupFirstAccount": "İlk hesabınızı ekleyin",
|
||||
"importScanQrCode": "Karekod tara",
|
||||
"importScanQrCode": "QR kod tara",
|
||||
"qrCode": "QR Kodu",
|
||||
"importEnterSetupKey": "Kurulum anahtarını giriniz",
|
||||
"importAccountPageTitle": "Hesap bilgilerinizi girin",
|
||||
@@ -51,11 +51,11 @@
|
||||
"trashCode": "Kod çöpe atılsın mı?",
|
||||
"trashCodeMessage": "{account} için kodu çöpe atmak istediğinize emin misiniz?",
|
||||
"trash": "Çöp",
|
||||
"viewLogsAction": "Günlüğü görüntüle",
|
||||
"sendLogsDescription": "Günlüğünüz hatanızı çözmemize yardımcı olacaktır. Hassas bilginin kaydedilmediğine dikkat etsek de bu günlükleri paylaşmadan önce kontrol etmenizi isteriz.",
|
||||
"preparingLogsTitle": "Günlük hazırlanıyor...",
|
||||
"emailLogsTitle": "Günlüğü e-posta olarak gönder",
|
||||
"emailLogsMessage": "Lütfen günlüğünüzü {email} adresine gönderin",
|
||||
"viewLogsAction": "Kayıtları görüntüle",
|
||||
"sendLogsDescription": "Kayıtlarınız hatanızı çözmemize yardımcı olacaktır. Hassas bilginin kaydedilmediğine dikkat etsek de bu günlükleri paylaşmadan önce kontrol etmenizi isteriz.",
|
||||
"preparingLogsTitle": "Kayıtlar hazırlanıyor...",
|
||||
"emailLogsTitle": "Kayıtları e-posta olarak gönder",
|
||||
"emailLogsMessage": "Lütfen kayıtlarınızı {email} adresine gönderin",
|
||||
"@emailLogsMessage": {
|
||||
"placeholders": {
|
||||
"email": {
|
||||
@@ -64,7 +64,7 @@
|
||||
}
|
||||
},
|
||||
"copyEmailAction": "E-postayı Kopyala",
|
||||
"exportLogsAction": "Günlüğü dışa aktar",
|
||||
"exportLogsAction": "Kayıtları dışa aktar",
|
||||
"reportABug": "Hata bildirin",
|
||||
"crashAndErrorReporting": "Çökme ve hata bildirimi",
|
||||
"reportBug": "Hata bildir",
|
||||
@@ -90,7 +90,7 @@
|
||||
"welcomeBack": "Tekrar hoş geldiniz!",
|
||||
"emailAlreadyRegistered": "E-posta zaten kayıtlı.",
|
||||
"emailNotRegistered": "E-posta kayıtlı değil.",
|
||||
"madeWithLoveAtPrefix": "❤️ ile şurada yapılmıştır ",
|
||||
"madeWithLoveAtPrefix": "❤️ ile yapılmıştır ",
|
||||
"supportDevs": "Bu projeyi desteklemek için <bold-green>ente</bold-green> kanalına abone olun",
|
||||
"supportDiscount": "İlk yılda %10 indirim için \"AUTH\" kupon kodunu kullanın",
|
||||
"changeEmail": "E-posta adresini değiştir",
|
||||
@@ -176,7 +176,7 @@
|
||||
"invalidEmailTitle": "Geçersiz e-posta adresi",
|
||||
"invalidEmailMessage": "Lütfen geçerli bir e-posta adresi girin.",
|
||||
"deleteAccount": "Hesabı sil",
|
||||
"deleteAccountQuery": "Sizin gitmenizi görmekten üzüleceğiz. Bazı problemlerle mi karşılaşıyorsunuz?",
|
||||
"deleteAccountQuery": "Sizin gittiğinizi görmekten üzüleceğiz. Bazı problemlerle mi karşılaşıyorsunuz?",
|
||||
"yesSendFeedbackAction": "Evet, geri bildirimi gönder",
|
||||
"noDeleteAccountAction": "Hayır, hesabı sil",
|
||||
"initiateAccountDeleteTitle": "Hesap silme işlemini yapabilmek için lütfen kimliğinizi doğrulayın",
|
||||
@@ -253,8 +253,8 @@
|
||||
"insecureDevice": "Güvenli olmayan cihaz",
|
||||
"sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Üzgünüz, bu cihazda güvenli anahtarlar oluşturamadık.\n\nlütfen farklı bir cihazdan kaydolun.",
|
||||
"howItWorks": "Nasıl çalışır",
|
||||
"ackPasswordLostWarning": "Eğer şifremi kaybedersem, verilerim <underline>uçtan uca şifrelendiğinden</underline> verilerimi kaybedebileceğimi anladım.",
|
||||
"loginTerms": "Giriş yaparak, <u-terms>kullanım şartları</u-terms>nı ve <u-policy>gizlilik politikası</u-policy>nı onaylıyorum",
|
||||
"ackPasswordLostWarning": "Eğer şifremi kaybedersem, verilerim <underline> uçtan uca şifrelendiğinden </underline> verilerimi kaybedebileceğimi anladım.",
|
||||
"loginTerms": "Giriş yaparak, <u-terms> kullanım şartları </u-terms>nı ve <u-policy> gizlilik politikası </u-policy>nı onaylıyorum",
|
||||
"logInLabel": "Giriş yapın",
|
||||
"logout": "Çıkış yap",
|
||||
"areYouSureYouWantToLogout": "Çıkış yapmak istediğinize emin misiniz?",
|
||||
@@ -277,10 +277,10 @@
|
||||
"recoveryKeyVerifyReason": "Kurtarma anahtarınız, şifrenizi unutmanız durumunda fotoğraflarınızı kurtarmanın tek yoludur. Kurtarma anahtarınızı Ayarlar > Hesap bölümünde bulabilirsiniz.\n\nDoğru kaydettiğinizi doğrulamak için lütfen kurtarma anahtarınızı buraya girin.",
|
||||
"confirmYourRecoveryKey": "Kurtarma anahtarınızı doğrulayın",
|
||||
"confirm": "Doğrula",
|
||||
"emailYourLogs": "Günlüklerinizi e-postayla gönderin",
|
||||
"pleaseSendTheLogsTo": "Lütfen günlükleri şu adrese gönderin\n{toEmail}",
|
||||
"emailYourLogs": "Kayıtlarınızı e-postayla gönderin",
|
||||
"pleaseSendTheLogsTo": "Lütfen kayıtları şu adrese gönderin\n{toEmail}",
|
||||
"copyEmailAddress": "E-posta adresini kopyala",
|
||||
"exportLogs": "Günlüğü dışa aktar",
|
||||
"exportLogs": "Kayıtları dışa aktar",
|
||||
"enterYourRecoveryKey": "Kurtarma anahtarınızı girin",
|
||||
"tempErrorContactSupportIfPersists": "Bir şeyler ters gitmiş gibi görünüyor. Lütfen bir süre sonra tekrar deneyin. Hata devam ederse, lütfen destek ekibimizle iletişime geçin.",
|
||||
"networkHostLookUpErr": "Ente'ye bağlanılamıyor, lütfen ağ ayarlarınızı kontrol edin ve hata devam ederse desteğe başvurun.",
|
||||
@@ -499,10 +499,24 @@
|
||||
"appLockOfflineModeWarning": "Yedekleme olmadan devam etmeyi seçtiniz. Eğer uygulama parolanızı unutursanız, verilerinize erişiminiz engellenir.",
|
||||
"duplicateCodes": "Yinelenen kodlar",
|
||||
"noDuplicates": "✨ Yinelenen yok",
|
||||
"youveNoDuplicateCodesThatCanBeCleared": "Temizlenebilecek herhangi bir yinelenen kodunuz yok",
|
||||
"deduplicateCodes": "Kodları tekilleştir",
|
||||
"deselectAll": "Tümünün seçimini kaldır",
|
||||
"selectAll": "Tümünü seç",
|
||||
"deleteDuplicates": "Yinelenenleri sil",
|
||||
"plainHTML": "Sade HTML",
|
||||
"supportEnte": "<bold-Green>Ente</bold-Green>'yi destekle"
|
||||
"tellUsWhatYouThink": "Bize ne düşündüğünü söyle",
|
||||
"dropReviewiOS": "App Store'da bir inceleme bırakın",
|
||||
"dropReviewAndroid": "Play Store'da bir inceleme bırakın",
|
||||
"supportEnte": "<bold-Green>Ente</bold-Green>'yi destekle",
|
||||
"giveUsAStarOnGithub": "Github'da bize bir yıldız verin",
|
||||
"free5GB": "<bold-green>ente</bold-green> Fotoğraflarında 5GB ücretsiz",
|
||||
"loginWithAuthAccount": "Kimlik Doğrulama hesabınızla giriş yapın",
|
||||
"freeStorageOffer": "<bold-green>ente</bold-green> fotoğraflarında %10 indirim",
|
||||
"freeStorageOfferDescription": "İlk yılda %10 indirim almak için \"AUTH\" kodunu kullanın",
|
||||
"advanced": "Gelişmiş",
|
||||
"algorithm": "Algoritma",
|
||||
"type": "Tür",
|
||||
"period": "Zaman Aralığı",
|
||||
"digits": "Uzunluk"
|
||||
}
|
||||
@@ -173,6 +173,7 @@
|
||||
"invalidQRCode": "二维码无效",
|
||||
"noRecoveryKeyTitle": "没有恢复密钥吗?",
|
||||
"enterEmailHint": "请输入您的电子邮件地址",
|
||||
"enterNewEmailHint": "请输入您的新电子邮件地址",
|
||||
"invalidEmailTitle": "无效的电子邮件地址",
|
||||
"invalidEmailMessage": "请输入一个有效的电子邮件地址。",
|
||||
"deleteAccount": "删除账户",
|
||||
@@ -513,5 +514,10 @@
|
||||
"free5GB": "<bold-green>ente</bold-green> Photos 上 5GB 可用空间",
|
||||
"loginWithAuthAccount": "使用您的认证账户登录",
|
||||
"freeStorageOffer": "购买 <bold-green>ente</bold-green> Photos 可享受 10% 优惠",
|
||||
"freeStorageOfferDescription": "使用优惠码“AUTH”可享受首年 10% 折扣"
|
||||
"freeStorageOfferDescription": "使用优惠码“AUTH”可享受首年 10% 折扣",
|
||||
"advanced": "高级",
|
||||
"algorithm": "算法",
|
||||
"type": "类型",
|
||||
"period": "周期",
|
||||
"digits": "数字"
|
||||
}
|
||||
@@ -513,5 +513,10 @@
|
||||
"free5GB": "<bold-green>ente</bold-green> Photos 上 5GB 可用空間",
|
||||
"loginWithAuthAccount": "使用您的認證帳戶登錄",
|
||||
"freeStorageOffer": "購買 <bold-green>ente</bold-green> Photos 可享受 10% 優惠",
|
||||
"freeStorageOfferDescription": "使用優惠碼“AUTH”可享受首年 10% 折扣"
|
||||
"freeStorageOfferDescription": "使用優惠碼“AUTH”可享受首年 10% 折扣",
|
||||
"advanced": "進階",
|
||||
"algorithm": "演算法",
|
||||
"type": "類型",
|
||||
"period": "期間",
|
||||
"digits": "數位"
|
||||
}
|
||||
@@ -38,28 +38,28 @@ const List<Locale> appSupportedLocales = <Locale>[
|
||||
];
|
||||
|
||||
Locale? autoDetectedLocale;
|
||||
Locale localResolutionCallBack(locales, supportedLocales) {
|
||||
Locale? languageCodeMatch;
|
||||
final Map<String, Locale> languageCodeToLocale = {
|
||||
for (Locale supportedLocale in appSupportedLocales)
|
||||
supportedLocale.languageCode: supportedLocale,
|
||||
};
|
||||
|
||||
for (Locale locale in locales) {
|
||||
// This function takes device locales and supported locales as input
|
||||
// and returns the best matching locale.
|
||||
// The device locales are sorted by priority, so the first one is the most preferred.
|
||||
Locale localResolutionCallBack(onDeviceLocales, supportedLocales) {
|
||||
final Set<String> languageSupport = {};
|
||||
for (Locale supportedLocale in appSupportedLocales) {
|
||||
languageSupport.add(supportedLocale.languageCode);
|
||||
}
|
||||
for (Locale locale in onDeviceLocales) {
|
||||
// check if exact local is supported, if yes, return it
|
||||
if (appSupportedLocales.contains(locale)) {
|
||||
autoDetectedLocale = locale;
|
||||
return locale;
|
||||
}
|
||||
|
||||
if (languageCodeMatch == null &&
|
||||
languageCodeToLocale.containsKey(locale.languageCode)) {
|
||||
languageCodeMatch = languageCodeToLocale[locale.languageCode];
|
||||
autoDetectedLocale = languageCodeMatch;
|
||||
// check if language code is supported, if yes, return it
|
||||
if (languageSupport.contains(locale.languageCode)) {
|
||||
autoDetectedLocale = locale;
|
||||
return locale;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the first language code match or default to 'en'
|
||||
return languageCodeMatch ?? const Locale('en');
|
||||
return autoDetectedLocale ?? const Locale('en');
|
||||
}
|
||||
|
||||
Future<Locale?> getLocale({
|
||||
|
||||
@@ -293,10 +293,6 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
widget.code == null
|
||||
? advanceOptionWidget()
|
||||
: const SizedBox.shrink(),
|
||||
const SizedBox(height: 12),
|
||||
Wrap(
|
||||
spacing: 12,
|
||||
alignment: WrapAlignment.start,
|
||||
@@ -343,6 +339,10 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
widget.code == null
|
||||
? advanceOptionWidget()
|
||||
: const SizedBox.shrink(),
|
||||
const SizedBox(height: 40),
|
||||
SizedBox(
|
||||
width: 400,
|
||||
@@ -525,6 +525,7 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
||||
}
|
||||
|
||||
Widget advanceOptionWidget() {
|
||||
final l10n = context.l10n;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 16.0),
|
||||
child: Column(
|
||||
@@ -537,9 +538,7 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text(
|
||||
'Advanced',
|
||||
),
|
||||
Text(l10n.advanced),
|
||||
ValueListenableBuilder<bool>(
|
||||
valueListenable: showAdvancedOptions,
|
||||
builder: (context, isExpanded, child) {
|
||||
@@ -583,7 +582,7 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
const FieldLabel("Algorithm", width: 60),
|
||||
FieldLabel(l10n.algorithm, width: 60),
|
||||
AlgorithmSelectorWidget(
|
||||
currentAlgorithm: _algorithm,
|
||||
onSelected: (newAlgorithm) async {
|
||||
@@ -597,7 +596,7 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
const FieldLabel("Type", width: 60),
|
||||
FieldLabel(l10n.type, width: 60),
|
||||
ToptSelectorWidget(
|
||||
currentTopt: _type,
|
||||
onSelected: (newTopt) async {
|
||||
@@ -610,7 +609,7 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
const FieldLabel("Period", width: 60),
|
||||
FieldLabel(l10n.period, width: 60),
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
keyboardType: TextInputType.number,
|
||||
@@ -639,7 +638,7 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
const FieldLabel("Digits", width: 60),
|
||||
FieldLabel(l10n.digits, width: 60),
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
keyboardType: TextInputType.number,
|
||||
|
||||
@@ -13,6 +13,7 @@ import 'package:ente_auth/models/authenticator/auth_entity.dart';
|
||||
import 'package:ente_auth/models/authenticator/auth_key.dart';
|
||||
import 'package:ente_auth/models/authenticator/entity_result.dart';
|
||||
import 'package:ente_auth/models/authenticator/local_auth_entity.dart';
|
||||
import 'package:ente_auth/services/preference_service.dart';
|
||||
import 'package:ente_auth/store/authenticator_db.dart';
|
||||
import 'package:ente_auth/store/offline_authenticator_db.dart';
|
||||
import 'package:ente_crypto_dart/ente_crypto_dart.dart';
|
||||
@@ -194,8 +195,13 @@ class AuthenticatorService {
|
||||
final int lastSyncTime = _prefs.getInt(_lastEntitySyncTime) ?? 0;
|
||||
_logger.info("Current sync is $lastSyncTime");
|
||||
const int fetchLimit = 500;
|
||||
final List<AuthEntity> result =
|
||||
late final List<AuthEntity> result;
|
||||
late final int? epochTimeInMicroseconds;
|
||||
(result, epochTimeInMicroseconds) =
|
||||
await _gateway.getDiff(lastSyncTime, limit: fetchLimit);
|
||||
PreferenceService.instance
|
||||
.computeAndStoreTimeOffset(epochTimeInMicroseconds);
|
||||
|
||||
_logger.info("${result.length} entries fetched from remote");
|
||||
if (result.isEmpty) {
|
||||
return;
|
||||
|
||||
@@ -18,6 +18,7 @@ class PreferenceService {
|
||||
late final SharedPreferences _prefs;
|
||||
|
||||
static const kHasShownCoachMarkKey = "has_shown_coach_mark_v2";
|
||||
static const kLocalTimeOffsetKey = "local_time_offset";
|
||||
static const kShouldShowLargeIconsKey = "should_show_large_icons";
|
||||
static const kShouldHideCodesKey = "should_hide_codes";
|
||||
static const kShouldAutoFocusOnSearchBar = "should_auto_focus_on_search_bar";
|
||||
@@ -114,4 +115,24 @@ class PreferenceService {
|
||||
return installedTimeinMillis;
|
||||
}
|
||||
}
|
||||
|
||||
// localEpochOffsetInMilliSecond returns the local epoch offset in milliseconds.
|
||||
// This is used to adjust the time for TOTP calculations when device local time is not in sync with actual time.
|
||||
int timeOffsetInMilliSeconds() {
|
||||
return _prefs.getInt(kLocalTimeOffsetKey) ?? 0;
|
||||
}
|
||||
|
||||
void computeAndStoreTimeOffset(
|
||||
int? epochTimeInMicroseconds,
|
||||
) {
|
||||
if (epochTimeInMicroseconds == null) {
|
||||
_prefs.remove(kLocalTimeOffsetKey);
|
||||
return;
|
||||
}
|
||||
int serverEpochTimeInMilliSecond = epochTimeInMicroseconds ~/ 1000;
|
||||
int localEpochTimeInMilliSecond = DateTime.now().millisecondsSinceEpoch;
|
||||
int localEpochOffset =
|
||||
serverEpochTimeInMilliSecond - localEpochTimeInMilliSecond;
|
||||
_prefs.setInt(kLocalTimeOffsetKey, localEpochOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ class _ChangeEmailDialogState extends State<ChangeEmailDialog> {
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return AlertDialog(
|
||||
title: Text(l10n.enterEmailHint),
|
||||
title: Text(l10n.enterNewEmailHint),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
|
||||
@@ -7,10 +7,12 @@ import 'package:flutter/material.dart';
|
||||
class CodeTimerProgress extends StatefulWidget {
|
||||
final int period;
|
||||
final bool isCompactMode;
|
||||
final int timeOffsetInMilliseconds;
|
||||
const CodeTimerProgress({
|
||||
super.key,
|
||||
required this.period,
|
||||
this.isCompactMode = false,
|
||||
this.timeOffsetInMilliseconds = 0,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -20,7 +22,7 @@ class CodeTimerProgress extends StatefulWidget {
|
||||
class _CodeTimerProgressState extends State<CodeTimerProgress> {
|
||||
late final Timer _timer;
|
||||
late final ValueNotifier<double> _progress;
|
||||
late final int _periodInMicros;
|
||||
late final int _periodInMilii;
|
||||
|
||||
// Reduce update frequency
|
||||
final int _updateIntervalMs =
|
||||
@@ -29,29 +31,30 @@ class _CodeTimerProgressState extends State<CodeTimerProgress> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_periodInMicros = widget.period * 1000000;
|
||||
_periodInMilii = widget.period * 1000;
|
||||
_progress = ValueNotifier<double>(0.0);
|
||||
_updateTimeRemaining(DateTime.now().microsecondsSinceEpoch);
|
||||
_updateTimeRemaining(DateTime.now().millisecondsSinceEpoch);
|
||||
|
||||
_timer = Timer.periodic(Duration(milliseconds: _updateIntervalMs), (timer) {
|
||||
final now = DateTime.now().microsecondsSinceEpoch;
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
_updateTimeRemaining(now);
|
||||
});
|
||||
}
|
||||
|
||||
void _updateTimeRemaining(int currentMicros) {
|
||||
void _updateTimeRemaining(int currentMilliSeconds) {
|
||||
// More efficient time calculation using modulo
|
||||
final elapsed = (currentMicros) % _periodInMicros;
|
||||
final timeRemaining = _periodInMicros - elapsed;
|
||||
_progress.value = timeRemaining / _periodInMicros;
|
||||
final elapsed = (currentMilliSeconds + widget.timeOffsetInMilliseconds) %
|
||||
_periodInMilii;
|
||||
final timeRemaining = _periodInMilii - elapsed;
|
||||
_progress.value = timeRemaining / _periodInMilii;
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant CodeTimerProgress oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (oldWidget.period != widget.period) {
|
||||
_periodInMicros = widget.period * 1000000;
|
||||
_updateTimeRemaining(DateTime.now().microsecondsSinceEpoch);
|
||||
_periodInMilii = widget.period * 1000;
|
||||
_updateTimeRemaining(DateTime.now().millisecondsSinceEpoch);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -152,6 +152,8 @@ class _CodeWidgetState extends State<CodeWidget> {
|
||||
key: ValueKey('period_${widget.code.period}'),
|
||||
period: widget.code.period,
|
||||
isCompactMode: widget.isCompactMode,
|
||||
timeOffsetInMilliseconds:
|
||||
PreferenceService.instance.timeOffsetInMilliSeconds(),
|
||||
),
|
||||
widget.isCompactMode
|
||||
? const SizedBox(height: 4)
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import 'package:ente_auth/models/code.dart';
|
||||
import 'package:ente_auth/services/preference_service.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:otp/otp.dart' as otp;
|
||||
import 'package:steam_totp/steam_totp.dart';
|
||||
|
||||
int millisecondsSinceEpoch() {
|
||||
return DateTime.now().millisecondsSinceEpoch +
|
||||
PreferenceService.instance.timeOffsetInMilliSeconds();
|
||||
}
|
||||
|
||||
String getOTP(Code code) {
|
||||
if (code.type == Type.steam || code.issuer.toLowerCase() == 'steam') {
|
||||
return _getSteamCode(code);
|
||||
@@ -12,7 +18,7 @@ String getOTP(Code code) {
|
||||
}
|
||||
return otp.OTP.generateTOTPCodeString(
|
||||
getSanitizedSecret(code.secret),
|
||||
DateTime.now().millisecondsSinceEpoch,
|
||||
millisecondsSinceEpoch(),
|
||||
length: code.digits,
|
||||
interval: code.period,
|
||||
algorithm: _getAlgorithm(code),
|
||||
@@ -34,7 +40,7 @@ String _getSteamCode(Code code, [bool isNext = false]) {
|
||||
final SteamTOTP steamtotp = SteamTOTP(secret: code.secret);
|
||||
|
||||
return steamtotp.generate(
|
||||
DateTime.now().millisecondsSinceEpoch ~/ 1000 + (isNext ? code.period : 0),
|
||||
millisecondsSinceEpoch() ~/ 1000 + (isNext ? code.period : 0),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -44,7 +50,7 @@ String getNextTotp(Code code) {
|
||||
}
|
||||
return otp.OTP.generateTOTPCodeString(
|
||||
getSanitizedSecret(code.secret),
|
||||
DateTime.now().millisecondsSinceEpoch + code.period * 1000,
|
||||
millisecondsSinceEpoch() + code.period * 1000,
|
||||
length: code.digits,
|
||||
interval: code.period,
|
||||
algorithm: _getAlgorithm(code),
|
||||
@@ -56,9 +62,7 @@ String getNextTotp(Code code) {
|
||||
// It returns the start time and a list of future codes.
|
||||
(int, List<String>) generateFutureTotpCodes(Code code, int count) {
|
||||
final int startTime =
|
||||
((DateTime.now().millisecondsSinceEpoch ~/ 1000) ~/ code.period) *
|
||||
code.period *
|
||||
1000;
|
||||
((millisecondsSinceEpoch() ~/ 1000) ~/ code.period) * code.period * 1000;
|
||||
final String secret = getSanitizedSecret(code.secret);
|
||||
final List<String> codes = [];
|
||||
if (code.type == Type.steam || code.issuer.toLowerCase() == 'steam') {
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<releases>
|
||||
<release version="4.4.0" date="2025-05-31" />
|
||||
<release version="4.3.8" date="2025-05-20" />
|
||||
<release version="4.2.4" date="2025-01-11" />
|
||||
<release version="4.0.3" date="2024-10-08" />
|
||||
</releases>
|
||||
@@ -33,4 +35,4 @@
|
||||
<color type="primary" scheme_preference="light">#ffffff</color>
|
||||
<color type="primary" scheme_preference="dark">#000000</color>
|
||||
</branding>
|
||||
</component>
|
||||
</component>
|
||||
|
||||
@@ -5,15 +5,15 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
|
||||
sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "76.0.0"
|
||||
version: "72.0.0"
|
||||
_macros:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.3"
|
||||
version: "0.3.2"
|
||||
adaptive_theme:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -26,10 +26,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
|
||||
sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.11.0"
|
||||
version: "6.7.0"
|
||||
ansicolor:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -90,10 +90,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.12.0"
|
||||
version: "2.11.0"
|
||||
auto_size_text:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -130,10 +130,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.1"
|
||||
build:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -202,10 +202,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.3.0"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -234,10 +234,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
version: "1.1.1"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -250,10 +250,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
version: "1.18.0"
|
||||
confetti:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -435,10 +435,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
|
||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.2"
|
||||
version: "1.3.1"
|
||||
ffi:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -944,18 +944,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
|
||||
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.8"
|
||||
version: "10.0.5"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
||||
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.9"
|
||||
version: "3.0.5"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1024,18 +1024,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: macros
|
||||
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
|
||||
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.3-main.0"
|
||||
version: "0.1.2-main.4"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
version: "0.12.16+1"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1056,10 +1056,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
version: "1.15.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1168,10 +1168,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.9.0"
|
||||
path_drawing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1472,7 +1472,7 @@ packages:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
version: "0.0.99"
|
||||
sodium:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1509,10 +1509,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
version: "1.10.0"
|
||||
sprintf:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1566,10 +1566,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
version: "1.11.1"
|
||||
steam_totp:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1590,10 +1590,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version: "2.1.2"
|
||||
stream_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1606,10 +1606,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
version: "1.2.0"
|
||||
styled_text:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1630,18 +1630,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
version: "1.2.1"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
version: "0.7.2"
|
||||
timezone:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1806,10 +1806,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
|
||||
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.3.1"
|
||||
version: "14.2.5"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1899,5 +1899,5 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.7.0-0 <4.0.0"
|
||||
dart: ">=3.5.0 <4.0.0"
|
||||
flutter: ">=3.24.0"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
name: ente_auth
|
||||
description: ente two-factor authenticator
|
||||
version: 4.3.6+437
|
||||
version: 4.4.0+440
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
|
||||
70
auth/scripts/release_tag.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Function to display usage
|
||||
usage() {
|
||||
echo "Usage: $0 tag"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Ensure a tag was provided
|
||||
[[ $# -eq 0 ]] && usage
|
||||
|
||||
# Exit immediately if a command exits with a non-zero status
|
||||
set -e
|
||||
|
||||
# Go to the project root directory
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
# Get the tag from the command line argument
|
||||
TAG=$1
|
||||
|
||||
# Define the appdata file path - use absolute path to avoid directory navigation issues
|
||||
PROJECT_ROOT=$(pwd)
|
||||
APPDATA_FILE="${PROJECT_ROOT}/linux/packaging/enteauth.appdata.xml"
|
||||
|
||||
# Get the version from the pubspec.yaml file and cut everything after the +
|
||||
VERSION=$(grep "^version:" pubspec.yaml | awk '{ print $2 }' | cut -d '+' -f 1)
|
||||
|
||||
PREFIX="auth-v"
|
||||
|
||||
# Ensure the tag has the correct prefix
|
||||
if [[ $TAG != $PREFIX* ]]; then
|
||||
echo "Invalid tag. tags must start with '$PREFIX'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ensure the tag version is in the pubspec.yaml file
|
||||
if [[ $TAG != *$VERSION ]]; then
|
||||
echo "Invalid tag."
|
||||
echo "The version $VERSION in pubspec doesn't match the version in tag $TAG"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Extract version number from the tag (remove prefix)
|
||||
TAG_VERSION=${TAG#$PREFIX}
|
||||
|
||||
# Check if this version is already in the releases section of the appdata.xml file
|
||||
if ! grep -q "<release version=\"$TAG_VERSION\"" "$APPDATA_FILE"; then
|
||||
echo "Adding release entry for version $TAG_VERSION to appdata.xml"
|
||||
|
||||
# Get today's date in YYYY-MM-DD format
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
|
||||
# Use a more reliable approach with awk instead of sed for cross-platform compatibility
|
||||
echo "Creating temporary file with updated content..."
|
||||
awk '/<releases>/{print $0; print " <release version=\"'"$TAG_VERSION"'\" date=\"'"$TODAY"'\" />"; next}1' "$APPDATA_FILE" > "${APPDATA_FILE}.tmp"
|
||||
mv "${APPDATA_FILE}.tmp" "$APPDATA_FILE"
|
||||
|
||||
echo "Added release entry for version $TAG_VERSION with date $TODAY"
|
||||
|
||||
# Stage and commit the updated appdata.xml file
|
||||
git add "$APPDATA_FILE"
|
||||
git commit -m "Add release $TAG_VERSION to appdata.xml"
|
||||
echo "Committed appdata.xml changes for version $TAG_VERSION"
|
||||
fi
|
||||
|
||||
# If all checks pass, create the tag
|
||||
git tag $TAG
|
||||
echo "Tag $TAG created."
|
||||
|
||||
exit 0
|
||||
@@ -83,13 +83,14 @@ func (c *Client) VerifySRPSession(
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (c *Client) SendEmailOTP(
|
||||
func (c *Client) SendLoginOTP(
|
||||
ctx context.Context,
|
||||
email string,
|
||||
) error {
|
||||
var res AuthorizationResponse
|
||||
payload := map[string]interface{}{
|
||||
"email": email,
|
||||
"email": email,
|
||||
"purpose": "login",
|
||||
}
|
||||
r, err := c.restClient.R().
|
||||
SetContext(ctx).
|
||||
|
||||
@@ -167,7 +167,7 @@ func (c *ClICtrl) verifyPassKey(ctx context.Context, authResp *api.Authorization
|
||||
}
|
||||
|
||||
func (c *ClICtrl) validateEmail(ctx context.Context, email string) (*api.AuthorizationResponse, error) {
|
||||
err := c.Client.SendEmailOTP(ctx, email)
|
||||
err := c.Client.SendLoginOTP(ctx, email)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
# CHANGELOG
|
||||
|
||||
## v1.7.13 (Unreleased)
|
||||
## v1.7.14 (Unreleased)
|
||||
|
||||
- .
|
||||
|
||||
## v1.7.13
|
||||
|
||||
- Generate streams for videos (beta)
|
||||
|
||||
> Streamable videos can be enabled in Preferences. For more details, see the
|
||||
> [video streaming FAQ](https://help.ente.io/photos/faq/video-streaming).
|
||||
|
||||
- Support Turkish translations.
|
||||
|
||||
## v1.7.12
|
||||
|
||||
- Improved video player with streaming support (for already processed videos).
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ente",
|
||||
"version": "1.7.13-beta",
|
||||
"version": "1.7.14-beta",
|
||||
"private": true,
|
||||
"description": "Desktop client for Ente Photos",
|
||||
"repository": "github:ente-io/photos-desktop",
|
||||
@@ -38,25 +38,26 @@
|
||||
"lru-cache": "^11.1.0",
|
||||
"next-electron-server": "^1.0.0",
|
||||
"node-stream-zip": "^1.15.0",
|
||||
"onnxruntime-node": "^1.20.1"
|
||||
"onnxruntime-node": "^1.20.1",
|
||||
"zod": "^3.25.23"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.25.1",
|
||||
"@tsconfig/node22": "^22.0.1",
|
||||
"@eslint/js": "^9.27.0",
|
||||
"@tsconfig/node22": "^22.0.2",
|
||||
"@types/auto-launch": "^5.0.5",
|
||||
"@types/ffmpeg-static": "^3.0.3",
|
||||
"ajv": "^8.17.1",
|
||||
"concurrently": "^9.1.2",
|
||||
"cross-env": "^7.0.3",
|
||||
"electron": "^36.1.0",
|
||||
"electron": "^36.3.2",
|
||||
"electron-builder": "^26.0.14",
|
||||
"eslint": "^9",
|
||||
"prettier": "3.5.3",
|
||||
"prettier-plugin-organize-imports": "^4.1.0",
|
||||
"prettier-plugin-packagejson": "^2.5.10",
|
||||
"shx": "^0.3.4",
|
||||
"prettier-plugin-packagejson": "^2.5.14",
|
||||
"shx": "^0.4.0",
|
||||
"typescript": "^5.8.3",
|
||||
"typescript-eslint": "^8.31.1"
|
||||
"typescript-eslint": "^8.32.1"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22",
|
||||
"productName": "ente"
|
||||
|
||||
@@ -78,6 +78,14 @@ export const allowWindowClose = (): void => {
|
||||
* We call this at the end of this file.
|
||||
*/
|
||||
const main = () => {
|
||||
// Workaround for Electron 36 not launching on some Linux distros. Remove
|
||||
// once fixed or otherwise mitigated upstream.
|
||||
//
|
||||
// https://github.com/electron/electron/issues/46538#issuecomment-2808806722
|
||||
if (process.platform == "linux") {
|
||||
app.commandLine.appendSwitch("gtk-version", "3");
|
||||
}
|
||||
|
||||
const gotTheLock = app.requestSingleInstanceLock();
|
||||
if (!gotTheLock) {
|
||||
app.quit();
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
openLogDirectory,
|
||||
selectDirectory,
|
||||
} from "./services/dir";
|
||||
import { ffmpegExec } from "./services/ffmpeg";
|
||||
import { ffmpegDetermineVideoDuration, ffmpegExec } from "./services/ffmpeg";
|
||||
import {
|
||||
fsExists,
|
||||
fsFindFiles,
|
||||
@@ -182,10 +182,10 @@ export const attachIPCHandlers = () => {
|
||||
"generateImageThumbnail",
|
||||
(
|
||||
_,
|
||||
dataOrPathOrZipItem: Uint8Array | string | ZipItem,
|
||||
pathOrZipItem: string | ZipItem,
|
||||
maxDimension: number,
|
||||
maxSize: number,
|
||||
) => generateImageThumbnail(dataOrPathOrZipItem, maxDimension, maxSize),
|
||||
) => generateImageThumbnail(pathOrZipItem, maxDimension, maxSize),
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
@@ -193,9 +193,15 @@ export const attachIPCHandlers = () => {
|
||||
(
|
||||
_,
|
||||
command: FFmpegCommand,
|
||||
dataOrPathOrZipItem: Uint8Array | string | ZipItem,
|
||||
pathOrZipItem: string | ZipItem,
|
||||
outputFileExtension: string,
|
||||
) => ffmpegExec(command, dataOrPathOrZipItem, outputFileExtension),
|
||||
) => ffmpegExec(command, pathOrZipItem, outputFileExtension),
|
||||
);
|
||||
|
||||
ipcMain.handle(
|
||||
"ffmpegDetermineVideoDuration",
|
||||
(_, pathOrZipItem: string | ZipItem) =>
|
||||
ffmpegDetermineVideoDuration(pathOrZipItem),
|
||||
);
|
||||
|
||||
// - Upload
|
||||
|
||||
1133
desktop/src/main/services/ffmpeg-worker.ts
Normal file
@@ -1,678 +1,77 @@
|
||||
import pathToFfmpeg from "ffmpeg-static";
|
||||
import { randomBytes } from "node:crypto";
|
||||
/**
|
||||
* @file A bridge to the ffmpeg utility process. This code runs in the main
|
||||
* process.
|
||||
*/
|
||||
|
||||
import { wrap } from "comlink";
|
||||
import fs from "node:fs/promises";
|
||||
import path, { basename } from "node:path";
|
||||
import type { FFmpegCommand, ZipItem } from "../../types/ipc";
|
||||
import log from "../log";
|
||||
import { execAsync } from "../utils/electron";
|
||||
import {
|
||||
deleteTempFileIgnoringErrors,
|
||||
makeFileForDataOrStreamOrPathOrZipItem,
|
||||
makeFileForStreamOrPathOrZipItem,
|
||||
makeTempFilePath,
|
||||
} from "../utils/temp";
|
||||
|
||||
/* Ditto in the web app's code (used by the Wasm FFmpeg invocation). */
|
||||
const ffmpegPathPlaceholder = "FFMPEG";
|
||||
const inputPathPlaceholder = "INPUT";
|
||||
const outputPathPlaceholder = "OUTPUT";
|
||||
import type { FFmpegUtilityProcess } from "./ffmpeg-worker";
|
||||
import { ffmpegUtilityProcessEndpoint } from "./workers";
|
||||
|
||||
/**
|
||||
* Run a FFmpeg command
|
||||
*
|
||||
* [Note: FFmpeg in Electron]
|
||||
*
|
||||
* There is a Wasm build of FFmpeg, but that is currently 10-20 times slower
|
||||
* that the native build. That is slow enough to be unusable for our purposes.
|
||||
* https://ffmpegwasm.netlify.app/docs/performance
|
||||
*
|
||||
* So the alternative is to bundle a FFmpeg executable binary with our app. e.g.
|
||||
*
|
||||
* yarn add fluent-ffmpeg ffmpeg-static ffprobe-static
|
||||
*
|
||||
* (we only use ffmpeg-static, the rest are mentioned for completeness' sake).
|
||||
*
|
||||
* Interestingly, Electron already bundles an binary FFmpeg library (it comes
|
||||
* from the ffmpeg fork maintained by Chromium).
|
||||
* https://chromium.googlesource.com/chromium/third_party/ffmpeg
|
||||
* https://stackoverflow.com/questions/53963672/what-version-of-ffmpeg-is-bundled-inside-electron
|
||||
*
|
||||
* This can be found in (e.g. on macOS) at
|
||||
*
|
||||
* $ file ente.app/Contents/Frameworks/Electron\ Framework.framework/Versions/Current/Libraries/libffmpeg.dylib
|
||||
* .../libffmpeg.dylib: Mach-O 64-bit dynamically linked shared library arm64
|
||||
*
|
||||
* But I'm not sure if our code is supposed to be able to use it, and how.
|
||||
* Return a handle to the ffmpeg utility process, starting it if needed.
|
||||
*/
|
||||
export const ffmpegUtilityProcess = () =>
|
||||
ffmpegUtilityProcessEndpoint().then((port) =>
|
||||
wrap<FFmpegUtilityProcess>(port),
|
||||
);
|
||||
|
||||
/**
|
||||
* Implement the IPC "ffmpegExec" contract, writing the input and output to
|
||||
* temporary files as needed, and then forward to the {@link ffmpegExec} running
|
||||
* in the utility process.
|
||||
*/
|
||||
export const ffmpegExec = async (
|
||||
command: FFmpegCommand,
|
||||
dataOrPathOrZipItem: Uint8Array | string | ZipItem,
|
||||
pathOrZipItem: string | ZipItem,
|
||||
outputFileExtension: string,
|
||||
): Promise<Uint8Array> => {
|
||||
): Promise<Uint8Array> =>
|
||||
withInputFile(pathOrZipItem, async (worker, inputFilePath) => {
|
||||
const outputFilePath = await makeTempFilePath(outputFileExtension);
|
||||
try {
|
||||
await worker.ffmpegExec(command, inputFilePath, outputFilePath);
|
||||
return await fs.readFile(outputFilePath);
|
||||
} finally {
|
||||
await deleteTempFileIgnoringErrors(outputFilePath);
|
||||
}
|
||||
});
|
||||
|
||||
export const withInputFile = async <T>(
|
||||
pathOrZipItem: string | ZipItem,
|
||||
f: (worker: FFmpegUtilityProcess, inputFilePath: string) => Promise<T>,
|
||||
): Promise<T> => {
|
||||
const worker = await ffmpegUtilityProcess();
|
||||
|
||||
const {
|
||||
path: inputFilePath,
|
||||
isFileTemporary: isInputFileTemporary,
|
||||
writeToTemporaryFile: writeToTemporaryInputFile,
|
||||
} = await makeFileForDataOrStreamOrPathOrZipItem(dataOrPathOrZipItem);
|
||||
} = await makeFileForStreamOrPathOrZipItem(pathOrZipItem);
|
||||
|
||||
const outputFilePath = await makeTempFilePath(outputFileExtension);
|
||||
try {
|
||||
await writeToTemporaryInputFile();
|
||||
|
||||
let resolvedCommand: string[];
|
||||
if (Array.isArray(command)) {
|
||||
resolvedCommand = command;
|
||||
} else {
|
||||
const isHDR = await isHDRVideo(inputFilePath);
|
||||
log.debug(() => [basename(inputFilePath), { isHDR }]);
|
||||
resolvedCommand = isHDR ? command.hdr : command.default;
|
||||
}
|
||||
|
||||
const cmd = substitutePlaceholders(
|
||||
resolvedCommand,
|
||||
inputFilePath,
|
||||
outputFilePath,
|
||||
);
|
||||
|
||||
await execAsync(cmd);
|
||||
|
||||
return await fs.readFile(outputFilePath);
|
||||
return await f(worker, inputFilePath);
|
||||
} finally {
|
||||
if (isInputFileTemporary)
|
||||
await deleteTempFileIgnoringErrors(inputFilePath);
|
||||
await deleteTempFileIgnoringErrors(outputFilePath);
|
||||
}
|
||||
};
|
||||
|
||||
const substitutePlaceholders = (
|
||||
command: string[],
|
||||
inputFilePath: string,
|
||||
outputFilePath: string,
|
||||
) =>
|
||||
command.map((segment) => {
|
||||
if (segment == ffmpegPathPlaceholder) {
|
||||
return ffmpegBinaryPath();
|
||||
} else if (segment == inputPathPlaceholder) {
|
||||
return inputFilePath;
|
||||
} else if (segment == outputPathPlaceholder) {
|
||||
return outputFilePath;
|
||||
} else {
|
||||
return segment;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Return the path to the `ffmpeg` binary.
|
||||
*
|
||||
* At runtime, the FFmpeg binary is present in a path like (macOS example):
|
||||
* `ente.app/Contents/Resources/app.asar.unpacked/node_modules/ffmpeg-static/ffmpeg`
|
||||
* Implement the IPC "ffmpegDetermineVideoDuration" contract, writing the input
|
||||
* to temporary files as needed, and then forward to the
|
||||
* {@link ffmpegDetermineVideoDuration} running in the utility process.
|
||||
*/
|
||||
const ffmpegBinaryPath = () => {
|
||||
// This substitution of app.asar by app.asar.unpacked is suggested by the
|
||||
// ffmpeg-static library author themselves:
|
||||
// https://github.com/eugeneware/ffmpeg-static/issues/16
|
||||
return pathToFfmpeg!.replace("app.asar", "app.asar.unpacked");
|
||||
};
|
||||
|
||||
/**
|
||||
* A variant of {@link ffmpegExec} adapted to work with streams so that it can
|
||||
* handle the MP4 conversion of large video files.
|
||||
*
|
||||
* @param inputFilePath The path to a file on the user's local file system. This
|
||||
* is the video we want to convert.
|
||||
*
|
||||
* @param outputFilePath The path to a file on the user's local file system where
|
||||
* we should write the converted MP4 video.
|
||||
*/
|
||||
export const ffmpegConvertToMP4 = async (
|
||||
inputFilePath: string,
|
||||
outputFilePath: string,
|
||||
): Promise<void> => {
|
||||
const command = [
|
||||
ffmpegPathPlaceholder,
|
||||
"-i",
|
||||
inputPathPlaceholder,
|
||||
"-preset",
|
||||
"ultrafast",
|
||||
outputPathPlaceholder,
|
||||
];
|
||||
|
||||
const cmd = substitutePlaceholders(command, inputFilePath, outputFilePath);
|
||||
|
||||
await execAsync(cmd);
|
||||
};
|
||||
|
||||
export interface FFmpegGenerateHLSPlaylistAndSegmentsResult {
|
||||
playlistPath: string;
|
||||
videoPath: string;
|
||||
dimensions: { width: number; height: number };
|
||||
videoSize: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A bespoke variant of {@link ffmpegExec} for generation of HLS playlists for
|
||||
* videos.
|
||||
*
|
||||
* Overview of the cases:
|
||||
*
|
||||
* H.264, <= 10 MB - Skip
|
||||
* H.264, <= 4000 kb/s bitrate - Don't re-encode video stream
|
||||
* BT.709, <= 2000 kb/s bitrate - Don't apply the scale+fps filter
|
||||
* !BT.709 - Apply tonemap (zscale+tonemap+zscale)
|
||||
*
|
||||
* Example invocation:
|
||||
*
|
||||
* ffmpeg -i in.mov -vf 'scale=-2:720,fps=30,zscale=transfer=linear,tonemap=tonemap=hable:desat=0,zscale=primaries=709:transfer=709:matrix=709,format=yuv420p' -c:v libx264 -c:a aac -f hls -hls_key_info_file out.m3u8.info -hls_list_size 0 -hls_flags single_file out.m3u8
|
||||
*
|
||||
* See: [Note: Preview variant of videos]
|
||||
*
|
||||
* @param inputFilePath The path to a file on the user's local file system. This
|
||||
* is the video we want to generate an streamable HLS playlist for.
|
||||
*
|
||||
* @param outputPathPrefix The path to unique, unused and temporary prefix on
|
||||
* the user's local file system. This function will write the generated HLS
|
||||
* playlist and video segments under this prefix.
|
||||
*
|
||||
* @returns The paths to two files on the user's local file system - one
|
||||
* containing the generated HLS playlist, and the other containing the
|
||||
* transcoded and encrypted video segments that the HLS playlist refers to.
|
||||
*
|
||||
* If the video is such that it doesn't require stream generation, then this
|
||||
* function returns `undefined`.
|
||||
*/
|
||||
export const ffmpegGenerateHLSPlaylistAndSegments = async (
|
||||
inputFilePath: string,
|
||||
outputPathPrefix: string,
|
||||
): Promise<FFmpegGenerateHLSPlaylistAndSegmentsResult | undefined> => {
|
||||
const { isH264, isBT709, bitrate } =
|
||||
await detectVideoCharacteristics(inputFilePath);
|
||||
|
||||
log.debug(() => [basename(inputFilePath), { isH264, isBT709, bitrate }]);
|
||||
|
||||
// If the video is smaller than 10 MB, and already H.264 (the codec we are
|
||||
// going to use for the conversion), then a streaming variant is not much
|
||||
// use. Skip such cases.
|
||||
//
|
||||
// ---
|
||||
//
|
||||
// [Note: HEVC/H.265 issues]
|
||||
//
|
||||
// We've observed two issues out in the wild with HEVC videos:
|
||||
//
|
||||
// 1. On Linux, HEVC video streams don't play. However, since the audio
|
||||
// stream plays, the browser tells us that the "video" itself is
|
||||
// playable, but the user sees a blank screen with only audio.
|
||||
//
|
||||
// 2. HEVC + HDR videos taken on an iPhone have a rotation (`Side data:
|
||||
// displaymatrix` in the ffmpeg output) that Chrome (and thus Electron)
|
||||
// doesn't take into account, so these play upside down.
|
||||
//
|
||||
// Not fully related to this case, but mentioning here as to why both the
|
||||
// size and codec need to be checked before skipping stream generation.
|
||||
if (isH264) {
|
||||
const inputVideoSize = await fs
|
||||
.stat(inputFilePath)
|
||||
.then((st) => st.size);
|
||||
if (inputVideoSize <= 10 * 1024 * 1024 /* 10 MB */) {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// If the video is already H.264 with a bitrate less than 4000 kbps, then we
|
||||
// do not need to reencode the video stream (by _far_ the costliest part of
|
||||
// the HLS stream generation).
|
||||
const reencodeVideo = !(isH264 && bitrate && bitrate <= 4000 * 1000);
|
||||
|
||||
// If the bitrate is not too high, then we don't need to rescale the video
|
||||
// when generating the video stream. This is not a performance optimization,
|
||||
// but more for avoiding making the video size smaller unnecessarily.
|
||||
const rescaleVideo = !(bitrate && bitrate <= 2000 * 1000);
|
||||
|
||||
// [Note: Tonemapping HDR to HD]
|
||||
//
|
||||
// BT.709 ("HD") is a standard that describes things like how color is
|
||||
// encoded, the range of values, and their "meaning" - i.e. how to map the
|
||||
// values in the video to the pixels on the screen.
|
||||
//
|
||||
// It is not the only such standard, there are three common examples:
|
||||
//
|
||||
// - BT.601 ("Standard-Definition" or SD)
|
||||
// - BT.709 ("High-Definition" or HD)
|
||||
// - BT.2020 ("Ultra-High-Definition" or UHD, aka HDR^).
|
||||
//
|
||||
// ^ HDR ("High-Dynamic-Range") is an addendum to BT.2020, but for our
|
||||
// purpose here we can treat it as as alias.
|
||||
//
|
||||
// BT.709 is the most common amongst these for older files out stored on
|
||||
// computers, and they conform mostly to the standard (one notable exception
|
||||
// is that the BT.709 standard also recommends using the yuv422p pixel
|
||||
// format, but de facto yuv420p is used because many video players only
|
||||
// support yuv420p).
|
||||
//
|
||||
// Since BT.709 is the most widely supported standard, we use it when
|
||||
// generating the HLS playlist so to allow playback across the widest
|
||||
// possible hardware/OS/browser combinations.
|
||||
//
|
||||
// If we convert HDR to HD without naively, then the colors look washed out
|
||||
// compared to the original. To resolve this, we use a ffmpeg filterchain
|
||||
// that uses the tonemap filter.
|
||||
//
|
||||
// However applying this tonemap to videos that are already HD leads to a
|
||||
// brightness drop. So we conditionally apply this filter chain only if the
|
||||
// colorspace is not already BT.709.
|
||||
//
|
||||
// See also: [Note: Alternative FFmpeg command for HDR videos], although
|
||||
// that uses a allow-list based check (while here we use deny-list).
|
||||
//
|
||||
// Reference:
|
||||
// - https://trac.ffmpeg.org/wiki/colorspace
|
||||
const tonemap = !isBT709;
|
||||
|
||||
// We want the generated playlist to refer to the chunks as "output.ts".
|
||||
//
|
||||
// So we arrange things accordingly: We use the `outputPathPrefix` as our
|
||||
// working directory, and then ask ffmpeg to generate a playlist with the
|
||||
// name "output.m3u8".
|
||||
//
|
||||
// ffmpeg will automatically place the segments in a file with the same base
|
||||
// name as the playlist, but with a ".ts" extension. And since we use the
|
||||
// "single_file" option, all the segments will be placed in a file named
|
||||
// "output.ts".
|
||||
|
||||
await fs.mkdir(outputPathPrefix);
|
||||
|
||||
const playlistPath = path.join(outputPathPrefix, "output.m3u8");
|
||||
const videoPath = path.join(outputPathPrefix, "output.ts");
|
||||
|
||||
// Generate a cryptographically secure random key (16 bytes).
|
||||
const keyBytes = randomBytes(16);
|
||||
const keyB64 = keyBytes.toString("base64");
|
||||
|
||||
// Convert it to a data: URI that will be added to the playlist.
|
||||
const keyURI = `data:text/plain;base64,${keyB64}`;
|
||||
|
||||
// Determine two paths - one where we will write the key itself, and where
|
||||
// we will write the "key info" that provides ffmpeg the `keyURI` and the
|
||||
// `keyPath;.
|
||||
const keyPath = playlistPath + ".key";
|
||||
const keyInfoPath = playlistPath + ".key-info";
|
||||
|
||||
// Generate a "key info":
|
||||
//
|
||||
// - the first line specifies the key URI that is written into the playlist.
|
||||
// - the second line specifies the path to the local file system file from
|
||||
// where ffmpeg should read the key.
|
||||
const keyInfo = [keyURI, keyPath].join("\n");
|
||||
|
||||
// Overview:
|
||||
//
|
||||
// - Video H.264 HD 720p 30fps.
|
||||
// - Audio AAC 128kbps.
|
||||
// - Encrypted HLS playlist with a single file containing all the chunks.
|
||||
//
|
||||
// Reference:
|
||||
// - `man ffmpeg-all`
|
||||
// - https://trac.ffmpeg.org/wiki/Encode/H.264
|
||||
//
|
||||
const command = [
|
||||
ffmpegBinaryPath(),
|
||||
// Reduce the amount of output lines we have to parse.
|
||||
["-hide_banner"],
|
||||
// Input file. We don't need any extra options that apply to the input file.
|
||||
"-i",
|
||||
inputFilePath,
|
||||
// The remaining options apply to the next output file (`playlistPath`).
|
||||
reencodeVideo
|
||||
? [
|
||||
// `-vf` creates a filter graph for the video stream. It is a
|
||||
// comma separated list of filters chained together, e.g.
|
||||
// `filter1=key=value:key=value.filter2=key=value`.
|
||||
"-vf",
|
||||
[
|
||||
// Do the rescaling to even number of pixels always if the
|
||||
// tonemapping is going to be applied subsequently,
|
||||
// otherwise the tonemapping will fail with "image
|
||||
// dimensions must be divisible by subsampling factor".
|
||||
//
|
||||
// While we add the extra condition here for completeness,
|
||||
// it won't usually matter since a non-BT.709 video is
|
||||
// likely using a new codec, and as such would've a high
|
||||
// enough bitrate to require rescaling anyways.
|
||||
rescaleVideo || tonemap
|
||||
? [
|
||||
// Scales the video to maximum 720p height,
|
||||
// keeping aspect ratio and the calculated
|
||||
// dimension divisible by 2 (some of the other
|
||||
// operations require an even pixel count).
|
||||
"scale=-2:720",
|
||||
// Convert the video to a constant 30 fps,
|
||||
// duplicating or dropping frames as necessary.
|
||||
"fps=30",
|
||||
]
|
||||
: [],
|
||||
// Convert the colorspace if the video is not in the HD
|
||||
// color space (bt709). Before conversion, tone map colors
|
||||
// so that they work the same across the change in the
|
||||
// dyamic range.
|
||||
//
|
||||
// 1. The tonemap filter only works linear light, so we
|
||||
// first use zscale with transfer=linear to linearize
|
||||
// the input.
|
||||
//
|
||||
// 2. Then we use the tonemap, with the hable option that
|
||||
// is best for preserving details. desat=0 turns off
|
||||
// the default desaturation.
|
||||
//
|
||||
// 3. Use zscale again to "convert to BT.709" by asking it
|
||||
// to set the all three of color primaries, transfer
|
||||
// characteristics and colorspace matrix to 709 (Note:
|
||||
// the constants specified in the tonemap filter help
|
||||
// do not include the "bt" prefix)
|
||||
//
|
||||
// See: https://ffmpeg.org/ffmpeg-filters.html#tonemap-1
|
||||
//
|
||||
// See: [Note: Tonemapping HDR to HD]
|
||||
tonemap
|
||||
? [
|
||||
"zscale=transfer=linear",
|
||||
"tonemap=tonemap=hable:desat=0",
|
||||
"zscale=primaries=709:transfer=709:matrix=709",
|
||||
]
|
||||
: [],
|
||||
// Output using the well supported pixel format: 8-bit YUV
|
||||
// planar color space with 4:2:0 chroma subsampling.
|
||||
"format=yuv420p",
|
||||
]
|
||||
.flat()
|
||||
.join(","),
|
||||
]
|
||||
: [],
|
||||
reencodeVideo
|
||||
? // Video codec H.264
|
||||
//
|
||||
// - `-c:v libx264` converts the video stream to the H.264 codec.
|
||||
//
|
||||
// - We don't supply a bitrate, instead it uses the default CRF
|
||||
// ("23") as recommended in the ffmpeg trac.
|
||||
//
|
||||
// - We don't supply a preset, it'll use the default ("medium").
|
||||
["-c:v", "libx264"]
|
||||
: // Keep the video stream unchanged
|
||||
["-c:v", "copy"],
|
||||
// Audio codec AAC
|
||||
//
|
||||
// - `-c:a aac` converts the audio stream to use the AAC codec
|
||||
//
|
||||
// - We don't supply a bitrate, it'll use the AAC default 128k bps.
|
||||
["-c:a", "aac"],
|
||||
// Generate a HLS playlist.
|
||||
["-f", "hls"],
|
||||
// Tell ffmpeg where to find the key, and the URI for the key to write
|
||||
// into the generated playlist. Implies "-hls_enc 1".
|
||||
["-hls_key_info_file", keyInfoPath],
|
||||
// Generate as many playlist entries as needed (default limit is 5).
|
||||
["-hls_list_size", "0"],
|
||||
// Place all the video segments within the same .ts file (with the same
|
||||
// path as the playlist file but with a ".ts" extension).
|
||||
["-hls_flags", "single_file"],
|
||||
// Output path where the playlist should be generated.
|
||||
playlistPath,
|
||||
].flat();
|
||||
|
||||
let dimensions: ReturnType<typeof detectVideoDimensions>;
|
||||
let videoSize: number;
|
||||
|
||||
try {
|
||||
// Write the key and the keyInfo to their desired paths.
|
||||
await Promise.all([
|
||||
fs.writeFile(keyPath, keyBytes),
|
||||
fs.writeFile(keyInfoPath, keyInfo, { encoding: "utf8" }),
|
||||
]);
|
||||
|
||||
// Run the ffmpeg command to generate the HLS playlist and segments.
|
||||
//
|
||||
// Note: Depending on the size of the input file, this may take long!
|
||||
const { stderr: conversionStderr } = await execAsync(command);
|
||||
|
||||
// Determine the dimensions of the generated video from the stderr
|
||||
// output produced by ffmpeg during the conversion.
|
||||
dimensions = detectVideoDimensions(conversionStderr);
|
||||
|
||||
// Find the size of the generated video segments by reading the size of
|
||||
// the generated .ts file.
|
||||
videoSize = await fs.stat(videoPath).then((st) => st.size);
|
||||
} catch (e) {
|
||||
log.error("HLS generation failed", e);
|
||||
await Promise.all([
|
||||
deleteTempFileIgnoringErrors(playlistPath),
|
||||
deleteTempFileIgnoringErrors(videoPath),
|
||||
]);
|
||||
throw e;
|
||||
} finally {
|
||||
await Promise.all([
|
||||
deleteTempFileIgnoringErrors(keyInfoPath),
|
||||
deleteTempFileIgnoringErrors(keyPath),
|
||||
// ffmpeg writes a /path/output.ts.tmp, clear it out too.
|
||||
deleteTempFileIgnoringErrors(videoPath + ".tmp"),
|
||||
]);
|
||||
}
|
||||
|
||||
return { playlistPath, videoPath, dimensions, videoSize };
|
||||
};
|
||||
|
||||
/**
|
||||
* A regex that matches the first line of the form
|
||||
*
|
||||
* Stream #0:0: Video: h264 (High 10) ([27][0][0][0] / 0x001B), yuv420p10le(tv, bt2020nc/bt2020/arib-std-b67), 1920x1080, 30 fps, 30 tbr, 90k tbn
|
||||
*
|
||||
* The part after Video: is the first capture group.
|
||||
*
|
||||
* Another example:
|
||||
*
|
||||
* Stream #0:1[0x2](und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(progressive), 480x270 [SAR 1:1 DAR 16:9], 539 kb/s, 29.97 fps, 29.97 tbr, 30k tbn (default)
|
||||
*/
|
||||
const videoStreamLineRegex = /Stream #.+: Video:(.+)\n/;
|
||||
|
||||
/** {@link videoStreamLineRegex}, but global. */
|
||||
const videoStreamLinesRegex = /Stream #.+: Video:(.+)\n/g;
|
||||
|
||||
/**
|
||||
* A regex that matches "<digits> kb/s" preceded by a space. See
|
||||
* {@link videoStreamLineRegex} for the context in which it is used.
|
||||
*/
|
||||
const videoBitrateRegex = / ([1-9]\d*) kb\/s/;
|
||||
|
||||
/**
|
||||
* A regex that matches <digits>x<digits> pair preceded by a space. See
|
||||
* {@link videoStreamLineRegex} for the context in which it is used.
|
||||
*
|
||||
* We constrain the digit sequence not to begin with 0 to exclude hexadecimal
|
||||
* representations of various constants that ffmpeg prints on this line (e.g.
|
||||
* "avc1 / 0x31637661").
|
||||
*/
|
||||
const videoDimensionsRegex = / ([1-9]\d*)x([1-9]\d*)/;
|
||||
|
||||
interface VideoCharacteristics {
|
||||
isH264: boolean;
|
||||
isBT709: boolean;
|
||||
bitrate: number | undefined;
|
||||
}
|
||||
/**
|
||||
* Heuristically determine information about the video at the given
|
||||
* {@link inputFilePath}:
|
||||
*
|
||||
* - If is encoded using H.264 codec.
|
||||
* - If it uses the BT.709 colorspace.
|
||||
* - Its bitrate.
|
||||
*
|
||||
* The defaults are tailored for the cases in which these conditions are used,
|
||||
* so that even if we get the detection wrong we'll only end up encoding videos
|
||||
* that could've possibly been skipped as an optimization.
|
||||
*
|
||||
* [Note: Parsing CLI output might break on ffmpeg updates]
|
||||
*
|
||||
* This function tries to determine the these bits of information about the
|
||||
* given video by scanning the ffmpeg info output for the video stream line, and
|
||||
* doing various string matches and regex extractions.
|
||||
*
|
||||
* Needless to say, while this works currently, this is liable to break in the
|
||||
* future. So if something stops working after updating ffmpeg, look here!
|
||||
*
|
||||
* Ideally, we'd have done this using `ffprobe`, but we don't have the ffprobe
|
||||
* binary at hand, so we make do by grepping the log output of ffmpeg.
|
||||
*
|
||||
* For reference,
|
||||
*
|
||||
* - codec and colorspace are printed by the `avcodec_string` function in the
|
||||
* ffmpeg source:
|
||||
* https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/avcodec.c
|
||||
*
|
||||
* - bitrate is printed by the `dump_stream_format` function in `dump.c`.
|
||||
*/
|
||||
const detectVideoCharacteristics = async (inputFilePath: string) => {
|
||||
const videoInfo = await pseudoFFProbeVideo(inputFilePath);
|
||||
const videoStreamLine = videoStreamLineRegex.exec(videoInfo)?.at(1)?.trim();
|
||||
|
||||
// Since the checks are heuristic, start with defaults that would cause the
|
||||
// codec conversion to happen, even if it is unnecessary.
|
||||
const res: VideoCharacteristics = {
|
||||
isH264: false,
|
||||
isBT709: false,
|
||||
bitrate: undefined,
|
||||
};
|
||||
if (!videoStreamLine) return res;
|
||||
|
||||
res.isH264 = videoStreamLine.startsWith("h264 ");
|
||||
res.isBT709 = videoStreamLine.includes("bt709");
|
||||
// The regex matches "\d kb/s", but there can be other units for the
|
||||
// bitrate. However, (a) "kb/s" is the most common for videos out in the
|
||||
// wild, and (b) even if we guess wrong it we'll just do "-v:c x264" instead
|
||||
// of "-v:c copy", so only unnecessary processing but no change in output.
|
||||
const brs = videoBitrateRegex.exec(videoStreamLine)?.at(0);
|
||||
if (brs) {
|
||||
const br = parseInt(brs, 10);
|
||||
if (br) res.bitrate = br;
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
/**
|
||||
* Heuristically detect the dimensions of the given video from the log output of
|
||||
* the ffmpeg invocation during the HLS playlist generation.
|
||||
*
|
||||
* This function tries to determine the width and height of the generated video
|
||||
* from the output log written by ffmpeg on its stderr during the generation
|
||||
* process, scanning it for the last video stream line, and trying to match a
|
||||
* "<digits>x<digits>" regex.
|
||||
*
|
||||
* See: [Note: Parsing CLI output might break on ffmpeg updates].
|
||||
*/
|
||||
const detectVideoDimensions = (conversionStderr: string) => {
|
||||
// There is a nicer way to do it - by running `pseudoFFProbeVideo` on the
|
||||
// generated playlist. However, that playlist includes a data URL that
|
||||
// specifies the encryption info, and ffmpeg refuses to read that unless we
|
||||
// specify the "-allowed_extensions ALL" or something to that effect.
|
||||
//
|
||||
// Unfortunately, our current ffmpeg binary (5.x) does not support that
|
||||
// option. So we instead parse the conversion output itself.
|
||||
//
|
||||
// This is also nice, since it saves on an extra ffmpeg invocation. But we
|
||||
// now need to be careful to find the right video stream line, since the
|
||||
// conversion output includes both the input and output video stream lines.
|
||||
//
|
||||
// To match the right (output) video stream line, we use a global regex, and
|
||||
// use the last match since that'd correspond to the single video stream
|
||||
// written in the output.
|
||||
const videoStreamLine = Array.from(
|
||||
conversionStderr.matchAll(videoStreamLinesRegex),
|
||||
)
|
||||
.at(-1) /* Last Stream...: Video: line in the output */
|
||||
?.at(1); /* First capture group */
|
||||
if (videoStreamLine) {
|
||||
const [, ws, hs] = videoDimensionsRegex.exec(videoStreamLine) ?? [];
|
||||
if (ws && hs) {
|
||||
const w = parseInt(ws, 10);
|
||||
const h = parseInt(hs, 10);
|
||||
if (w && h) {
|
||||
return { width: w, height: h };
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error(
|
||||
`Unable to detect video dimensions from stream line [${videoStreamLine ?? ""}]`,
|
||||
export const ffmpegDetermineVideoDuration = async (
|
||||
pathOrZipItem: string | ZipItem,
|
||||
): Promise<number> =>
|
||||
withInputFile(pathOrZipItem, async (worker, inputFilePath) =>
|
||||
worker.ffmpegDetermineVideoDuration(inputFilePath),
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Heuristically detect if the file at given path is a HDR video.
|
||||
*
|
||||
* This is similar to {@link detectVideoCharacteristics}, and see that
|
||||
* function's documentation for all the caveats. However, this function uses an
|
||||
* allow-list instead, and considers any file with color transfer "smpte2084" or
|
||||
* "arib-std-b67" to be HDR. While this is in some sense a more exact check, it
|
||||
* comes with different caveats:
|
||||
*
|
||||
* - These particular constants are not guaranteed to be correct; these are just
|
||||
* what I saw on the internet as being used / recommended for detecting HDR.
|
||||
*
|
||||
* - Since we don't have ffprobe, we're not checking the color space value
|
||||
* itself but a substring of the stream line in the ffmpeg stderr output.
|
||||
*
|
||||
* In particular, we use this more exact check for places where we have less
|
||||
* leeway. e.g. when generating thumbnails, if we apply the tonemapping to any
|
||||
* non-BT.709 file (as the HLS stream generation does), we start getting the
|
||||
* "code 3074: no path between colorspaces" error during the JPEG conversion
|
||||
* (this is not a problem in the H.264 conversion).
|
||||
*
|
||||
* - See: [Note: Alternative FFmpeg command for HDR videos]
|
||||
* - See: [Note: Tonemapping HDR to HD]
|
||||
*
|
||||
* @param inputFilePath The path to a video file on the user's machine.
|
||||
*
|
||||
* @returns `true` if this file is likely a HDR video. Exceptions are treated as
|
||||
* `false` to make this function safe to invoke without breaking the happy path.
|
||||
*/
|
||||
const isHDRVideo = async (inputFilePath: string) => {
|
||||
try {
|
||||
const videoInfo = await pseudoFFProbeVideo(inputFilePath);
|
||||
const vs = videoStreamLineRegex.exec(videoInfo)?.at(1);
|
||||
if (!vs) return false;
|
||||
return vs.includes("smpte2084") || vs.includes("arib-std-b67");
|
||||
} catch (e) {
|
||||
log.warn(`Could not detect HDR status of ${inputFilePath}`, e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the stderr of ffmpeg in an attempt to gain information about the video
|
||||
* at the given {@link inputFilePath}.
|
||||
*
|
||||
* We don't have the ffprobe binary at hand, which is why we need to use this
|
||||
* alternative. See: [Note: Parsing CLI output might break on ffmpeg updates]
|
||||
*
|
||||
* @returns the stderr of ffmpeg after running it on the input file. The exact
|
||||
* command we run is:
|
||||
*
|
||||
* ffmpeg -i in.mov -an -frames:v 0 -f null - 2>info.txt
|
||||
*
|
||||
* And the returned string is the contents of the `info.txt` thus produced.
|
||||
*/
|
||||
const pseudoFFProbeVideo = async (inputFilePath: string) => {
|
||||
const command = [
|
||||
ffmpegPathPlaceholder,
|
||||
// Reduce the amount of output lines we have to parse.
|
||||
["-hide_banner"],
|
||||
["-i", inputPathPlaceholder],
|
||||
"-an",
|
||||
["-frames:v", "0"],
|
||||
["-f", "null"],
|
||||
"-",
|
||||
].flat();
|
||||
|
||||
const cmd = substitutePlaceholders(command, inputFilePath, /* NA */ "");
|
||||
|
||||
const { stderr } = await execAsync(cmd);
|
||||
|
||||
return stderr;
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import { type ZipItem } from "../../types/ipc";
|
||||
import { execAsync, isDev } from "../utils/electron";
|
||||
import {
|
||||
deleteTempFileIgnoringErrors,
|
||||
makeFileForDataOrStreamOrPathOrZipItem,
|
||||
makeFileForStreamOrPathOrZipItem,
|
||||
makeTempFilePath,
|
||||
} from "../utils/temp";
|
||||
|
||||
@@ -61,7 +61,7 @@ const vipsPath = () =>
|
||||
);
|
||||
|
||||
export const generateImageThumbnail = async (
|
||||
dataOrPathOrZipItem: Uint8Array | string | ZipItem,
|
||||
pathOrZipItem: string | ZipItem,
|
||||
maxDimension: number,
|
||||
maxSize: number,
|
||||
): Promise<Uint8Array> => {
|
||||
@@ -69,7 +69,7 @@ export const generateImageThumbnail = async (
|
||||
path: inputFilePath,
|
||||
isFileTemporary: isInputFileTemporary,
|
||||
writeToTemporaryFile: writeToTemporaryInputFile,
|
||||
} = await makeFileForDataOrStreamOrPathOrZipItem(dataOrPathOrZipItem);
|
||||
} = await makeFileForStreamOrPathOrZipItem(pathOrZipItem);
|
||||
|
||||
const outputFilePath = await makeTempFilePath("jpeg");
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import log from "../log";
|
||||
import { clearPendingVideoResults } from "../stream";
|
||||
import { clearStores } from "./store";
|
||||
import { watchReset } from "./watch";
|
||||
import { terminateUtilityProcesses } from "./workers";
|
||||
import { clearOpenZipCache } from "./zip";
|
||||
|
||||
/**
|
||||
@@ -36,4 +37,9 @@ export const logout = (watcher: FSWatcher) => {
|
||||
} catch (e) {
|
||||
ignoreError("zip cache", e);
|
||||
}
|
||||
try {
|
||||
terminateUtilityProcesses();
|
||||
} catch (e) {
|
||||
ignoreError("utility processes", e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@ import { existsSync } from "fs";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import * as ort from "onnxruntime-node";
|
||||
import { z } from "zod";
|
||||
import log from "../log-worker";
|
||||
import { messagePortMainEndpoint } from "../utils/comlink";
|
||||
import { wait } from "../utils/common";
|
||||
@@ -23,6 +24,8 @@ import { fsStatMtime } from "./fs";
|
||||
|
||||
log.debugString("Started ML utility process");
|
||||
|
||||
process.on("uncaughtException", (e, origin) => log.error(origin, e));
|
||||
|
||||
process.parentPort.once("message", (e) => {
|
||||
// Initialize ourselves with the data we got from our parent.
|
||||
parseInitData(e.data);
|
||||
@@ -50,17 +53,10 @@ let _userDataPath: string | undefined;
|
||||
/** Equivalent to app.getPath("userData") */
|
||||
const userDataPath = () => _userDataPath!;
|
||||
|
||||
const MLWorkerInitData = z.object({ userDataPath: z.string() });
|
||||
|
||||
const parseInitData = (data: unknown) => {
|
||||
if (
|
||||
data &&
|
||||
typeof data == "object" &&
|
||||
"userDataPath" in data &&
|
||||
typeof data.userDataPath == "string"
|
||||
) {
|
||||
_userDataPath = data.userDataPath;
|
||||
} else {
|
||||
log.error("Unparseable initialization data");
|
||||
}
|
||||
_userDataPath = MLWorkerInitData.parse(data).userDataPath;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* utility processes that we create.
|
||||
*/
|
||||
|
||||
import type { Endpoint } from "comlink";
|
||||
import {
|
||||
MessageChannelMain,
|
||||
type BrowserWindow,
|
||||
@@ -12,12 +13,39 @@ import { app, utilityProcess } from "electron/main";
|
||||
import path from "node:path";
|
||||
import type { UtilityProcessType } from "../../types/ipc";
|
||||
import log, { processUtilityProcessLogMessage } from "../log";
|
||||
|
||||
/** The active ML utility process, if any. */
|
||||
let _child: UtilityProcess | undefined;
|
||||
import { messagePortMainEndpoint } from "../utils/comlink";
|
||||
|
||||
/**
|
||||
* Create a new ML utility process, terminating the older ones (if any).
|
||||
* Terminate any existing utility processes if they're running.
|
||||
*
|
||||
* This function is called during the logout sequence.
|
||||
*/
|
||||
export const terminateUtilityProcesses = () => {
|
||||
terminateMLProcessIfRunning();
|
||||
terminateFFmpegProcessIfRunning();
|
||||
};
|
||||
|
||||
/** The active ML utility process, if any. */
|
||||
let _utilityProcessML: UtilityProcess | undefined;
|
||||
|
||||
/** The active FFmpeg utility process, if any. */
|
||||
let _utilityProcessFFmpeg: UtilityProcess | undefined;
|
||||
|
||||
/**
|
||||
* A promise to a comlink {@link Endpoint} that can be used to communicate with
|
||||
* the active ffmpeg utility process (if any).
|
||||
*/
|
||||
let _utilityProcessFFmpegEndpoint: Promise<Endpoint> | undefined;
|
||||
|
||||
/**
|
||||
* Create a new utility process of the given {@link type}, terminating the older
|
||||
* ones (if any).
|
||||
*
|
||||
* Currently the only type is "ml". The following note explains the reasoning
|
||||
* why utility processes were used for the first workload (ML) that was handled
|
||||
* this way. Similar reasoning applies to subsequent workloads (ffmpeg) that
|
||||
* have been offloaded to utility processes in a slightly different manner to
|
||||
* avoid stutter in the UI.
|
||||
*
|
||||
* [Note: ML IPC]
|
||||
*
|
||||
@@ -75,33 +103,30 @@ let _child: UtilityProcess | undefined;
|
||||
export const triggerCreateUtilityProcess = (
|
||||
type: UtilityProcessType,
|
||||
window: BrowserWindow,
|
||||
) => {
|
||||
switch (type) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
case "ml":
|
||||
triggerCreateMLUtilityProcess(window);
|
||||
break;
|
||||
) => triggerCreateMLUtilityProcess(window);
|
||||
|
||||
const terminateMLProcessIfRunning = () => {
|
||||
if (_utilityProcessML) {
|
||||
log.debug(() => "Terminating running ML utility process");
|
||||
_utilityProcessML.kill();
|
||||
_utilityProcessML = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
export const triggerCreateMLUtilityProcess = (window: BrowserWindow) => {
|
||||
if (_child) {
|
||||
log.debug(() => "Terminating previous ML utility process");
|
||||
_child.kill();
|
||||
_child = undefined;
|
||||
}
|
||||
terminateMLProcessIfRunning();
|
||||
|
||||
const { port1, port2 } = new MessageChannelMain();
|
||||
|
||||
const child = utilityProcess.fork(path.join(__dirname, "ml-worker.js"));
|
||||
const userDataPath = app.getPath("userData");
|
||||
child.postMessage({ userDataPath }, [port1]);
|
||||
child.postMessage(/* MLWorkerInitData */ { userDataPath }, [port1]);
|
||||
|
||||
window.webContents.postMessage("utilityProcessPort/ml", undefined, [port2]);
|
||||
|
||||
handleMessagesFromUtilityProcess(child);
|
||||
handleMessagesFromMLUtilityProcess(child);
|
||||
|
||||
_child = child;
|
||||
_utilityProcessML = child;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -127,7 +152,7 @@ export const triggerCreateMLUtilityProcess = (window: BrowserWindow) => {
|
||||
* - When we need to communicate from the utility process to the main process,
|
||||
* we use the `parentPort` in the utility process.
|
||||
*/
|
||||
const handleMessagesFromUtilityProcess = (child: UtilityProcess) => {
|
||||
const handleMessagesFromMLUtilityProcess = (child: UtilityProcess) => {
|
||||
child.on("message", (m: unknown) => {
|
||||
if (processUtilityProcessLogMessage("[ml-worker]", m)) {
|
||||
return;
|
||||
@@ -135,3 +160,82 @@ const handleMessagesFromUtilityProcess = (child: UtilityProcess) => {
|
||||
log.info("Ignoring unknown message from ML utility process", m);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* A comlink endpoint that can be used to communicate with the ffmpeg utility
|
||||
* process. If there is no ffmpeg utility process, a new one is created on
|
||||
* demand.
|
||||
*
|
||||
* See [Note: ML IPC] for a general outline of why utility processes are needed
|
||||
* (tl;dr; to avoid stutter on the UI).
|
||||
*
|
||||
* In the case of ffmpeg, the IPC flow is a bit different: the utility process
|
||||
* is not exposed to the web layer, and is internal to the node layer. The
|
||||
* reason for this difference is that we need to create temporary files etc, and
|
||||
* doing it a utility process requires access to the `app` module which are not
|
||||
* accessible (See: [Note: Using Electron APIs in UtilityProcess]).
|
||||
*
|
||||
* There could've been possible reasonable workarounds, but the architecture
|
||||
* we've adopted of three layers:
|
||||
*
|
||||
* Renderer (web) <-> Node.js main <-> Node.js ffmpeg utility process
|
||||
*
|
||||
* The temporary file creation etc is handled in the Node.js main process, and
|
||||
* paths to the files are forwarded to the ffmpeg utility process to act on.
|
||||
*
|
||||
* @returns an endpoint that can be used to communicate with the utility
|
||||
* process. The utility process is expected to expose an object that conforms to
|
||||
* the {@link ElectronFFmpegWorkerNode} interface on this endpoint.
|
||||
*/
|
||||
export const ffmpegUtilityProcessEndpoint = () =>
|
||||
(_utilityProcessFFmpegEndpoint ??= createFFmpegUtilityProcessEndpoint());
|
||||
|
||||
const terminateFFmpegProcessIfRunning = () => {
|
||||
if (_utilityProcessFFmpeg) {
|
||||
log.debug(() => "Terminating running FFmpeg utility process");
|
||||
_utilityProcessFFmpeg.kill();
|
||||
_utilityProcessFFmpeg = undefined;
|
||||
_utilityProcessFFmpegEndpoint = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
const createFFmpegUtilityProcessEndpoint = () => {
|
||||
if (_utilityProcessFFmpeg) {
|
||||
throw new Error("FFmpeg utility process is already running");
|
||||
}
|
||||
|
||||
// Promise.withResolvers is currently in the node available to us.
|
||||
let resolve: ((endpoint: Endpoint) => void) | undefined;
|
||||
const promise = new Promise<Endpoint>((r) => (resolve = r));
|
||||
|
||||
const { port1, port2 } = new MessageChannelMain();
|
||||
|
||||
const child = utilityProcess.fork(path.join(__dirname, "ffmpeg-worker.js"));
|
||||
// Send a handle to the port (one end of the message channel) to the utility
|
||||
// process (alongwith any other init data). The utility process will reply
|
||||
// with an "ack" when it get it.
|
||||
const appVersion = app.getVersion();
|
||||
child.postMessage(/* FFmpegWorkerInitData */ { appVersion }, [port1]);
|
||||
|
||||
child.on("message", (m: unknown) => {
|
||||
if (m && typeof m == "object" && "method" in m) {
|
||||
switch (m.method) {
|
||||
case "ack":
|
||||
resolve!(messagePortMainEndpoint(port2));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (processUtilityProcessLogMessage("[ffmpeg-worker]", m)) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.info("Ignoring unknown message from ffmpeg utility process", m);
|
||||
});
|
||||
|
||||
_utilityProcessFFmpeg = child;
|
||||
|
||||
// Resolve with the other end of the message channel (once we get an "ack"
|
||||
// from the utility process).
|
||||
return promise;
|
||||
};
|
||||
|
||||
@@ -3,23 +3,18 @@
|
||||
*/
|
||||
import { net, protocol } from "electron/main";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import fs_ from "node:fs";
|
||||
import fs from "node:fs/promises";
|
||||
import { Readable, Writable } from "node:stream";
|
||||
import { Writable } from "node:stream";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import log from "./log";
|
||||
import {
|
||||
ffmpegConvertToMP4,
|
||||
ffmpegGenerateHLSPlaylistAndSegments,
|
||||
type FFmpegGenerateHLSPlaylistAndSegmentsResult,
|
||||
} from "./services/ffmpeg";
|
||||
import { ffmpegUtilityProcess } from "./services/ffmpeg";
|
||||
import { type FFmpegGenerateHLSPlaylistAndSegmentsResult } from "./services/ffmpeg-worker";
|
||||
import { markClosableZip, openZip } from "./services/zip";
|
||||
import { wait } from "./utils/common";
|
||||
import { writeStream } from "./utils/stream";
|
||||
import {
|
||||
deleteTempFile,
|
||||
deleteTempFileIgnoringErrors,
|
||||
makeFileForDataOrStreamOrPathOrZipItem,
|
||||
makeFileForStreamOrPathOrZipItem,
|
||||
makeTempFilePath,
|
||||
} from "./utils/temp";
|
||||
|
||||
@@ -234,12 +229,14 @@ export const clearPendingVideoResults = () => pendingVideoResults.clear();
|
||||
* See also: [Note: IPC streams]
|
||||
*/
|
||||
const handleConvertToMP4Write = async (request: Request) => {
|
||||
const worker = await ffmpegUtilityProcess();
|
||||
|
||||
const inputTempFilePath = await makeTempFilePath();
|
||||
await writeStream(inputTempFilePath, request.body!);
|
||||
|
||||
const outputTempFilePath = await makeTempFilePath("mp4");
|
||||
try {
|
||||
await ffmpegConvertToMP4(inputTempFilePath, outputTempFilePath);
|
||||
await worker.ffmpegConvertToMP4(inputTempFilePath, outputTempFilePath);
|
||||
} catch (e) {
|
||||
log.error("Conversion to MP4 failed", e);
|
||||
await deleteTempFileIgnoringErrors(outputTempFilePath);
|
||||
@@ -280,9 +277,9 @@ const handleVideoDone = async (token: string) => {
|
||||
*
|
||||
* The difference here is that we the conversion generates two streams^ - one
|
||||
* for the HLS playlist itself, and one for the file containing the encrypted
|
||||
* and transcoded video chunks. The video stream we write to the objectUploadURL
|
||||
* (provided via {@link params}), and then we return a JSON object containing
|
||||
* the token for the playlist, and other metadata for use by the renderer.
|
||||
* and transcoded video chunks. The video stream we write to the pre-signed
|
||||
* object upload URL(s), and then we return a JSON object containing the token
|
||||
* for the playlist, and other metadata for use by the renderer.
|
||||
*
|
||||
* ^ if the video doesn't require a stream to be generated (e.g. it is very
|
||||
* small and already uses a compatible codec) then a HTT 204 is returned and
|
||||
@@ -292,10 +289,12 @@ const handleGenerateHLSWrite = async (
|
||||
request: Request,
|
||||
params: URLSearchParams,
|
||||
) => {
|
||||
const objectUploadURL = params.get("objectUploadURL");
|
||||
if (!objectUploadURL) throw new Error("Missing objectUploadURL");
|
||||
const fileID = parseInt(params.get("fileID") ?? "", 10);
|
||||
const fetchURL = params.get("fetchURL");
|
||||
const authToken = params.get("authToken");
|
||||
if (!fileID || !fetchURL || !authToken) throw new Error("Missing params");
|
||||
|
||||
let inputItem: Parameters<typeof makeFileForDataOrStreamOrPathOrZipItem>[0];
|
||||
let inputItem: Parameters<typeof makeFileForStreamOrPathOrZipItem>[0];
|
||||
const path = params.get("path");
|
||||
if (path) {
|
||||
inputItem = path;
|
||||
@@ -311,20 +310,25 @@ const handleGenerateHLSWrite = async (
|
||||
}
|
||||
}
|
||||
|
||||
const worker = await ffmpegUtilityProcess();
|
||||
|
||||
const {
|
||||
path: inputFilePath,
|
||||
isFileTemporary: isInputFileTemporary,
|
||||
writeToTemporaryFile: writeToTemporaryInputFile,
|
||||
} = await makeFileForDataOrStreamOrPathOrZipItem(inputItem);
|
||||
} = await makeFileForStreamOrPathOrZipItem(inputItem);
|
||||
|
||||
const outputFilePathPrefix = await makeTempFilePath();
|
||||
let result: FFmpegGenerateHLSPlaylistAndSegmentsResult | undefined;
|
||||
try {
|
||||
await writeToTemporaryInputFile();
|
||||
|
||||
result = await ffmpegGenerateHLSPlaylistAndSegments(
|
||||
result = await worker.ffmpegGenerateHLSPlaylistAndSegments(
|
||||
inputFilePath,
|
||||
outputFilePathPrefix,
|
||||
fileID,
|
||||
fetchURL,
|
||||
authToken,
|
||||
);
|
||||
|
||||
if (!result) {
|
||||
@@ -332,115 +336,22 @@ const handleGenerateHLSWrite = async (
|
||||
return new Response(null, { status: 204 });
|
||||
}
|
||||
|
||||
const { playlistPath, videoPath, videoSize, dimensions } = result;
|
||||
try {
|
||||
await uploadVideoSegments(videoPath, videoSize, objectUploadURL);
|
||||
const { playlistPath, dimensions, videoSize, videoObjectID } = result;
|
||||
|
||||
const playlistToken = randomUUID();
|
||||
pendingVideoResults.set(playlistToken, playlistPath);
|
||||
const playlistToken = randomUUID();
|
||||
pendingVideoResults.set(playlistToken, playlistPath);
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({ playlistToken, dimensions, videoSize }),
|
||||
{ status: 200 },
|
||||
);
|
||||
} catch (e) {
|
||||
await deleteTempFileIgnoringErrors(playlistPath);
|
||||
throw e;
|
||||
} finally {
|
||||
await deleteTempFileIgnoringErrors(videoPath);
|
||||
}
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
playlistToken,
|
||||
dimensions,
|
||||
videoSize,
|
||||
videoObjectID,
|
||||
}),
|
||||
{ status: 200 },
|
||||
);
|
||||
} finally {
|
||||
if (isInputFileTemporary)
|
||||
await deleteTempFileIgnoringErrors(inputFilePath);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Upload the file at the given {@link videoFilePath} to the provided presigned
|
||||
* {@link objectUploadURL} using a HTTP PUT request.
|
||||
*
|
||||
* In case on non-HTTP-4xx errors, retry up to 3 times with exponential backoff.
|
||||
*
|
||||
* See: [Note: Upload HLS video segment from node side].
|
||||
*
|
||||
* @param videoFilePath The path to the file on the user's file system to
|
||||
* upload.
|
||||
*
|
||||
* @param videoSize The size in bytes of the file at {@link videoFilePath}.
|
||||
*
|
||||
* @param objectUploadURL A pre-signed URL to upload the file.
|
||||
*
|
||||
* ---
|
||||
*
|
||||
* This is an inlined but bespoke reimplementation of `retryEnsuringHTTPOkOr4xx`
|
||||
* from `web/packages/base/http.ts`
|
||||
*
|
||||
* - We don't have the rest of the scaffolding used by that function, which is
|
||||
* why it is intially inlined bespoked.
|
||||
*
|
||||
* - It handles the specific use case of uploading videos since generating the
|
||||
* HLS stream is a fairly expensive operation, so a retry to discount
|
||||
* transient network issues is called for. There are only 2 retries for a
|
||||
* total of 3 attempts, and the retry gaps are more spaced out.
|
||||
*
|
||||
* - Later it was discovered that net.fetch is much slower than node's native
|
||||
* fetch, so this implementation has further diverged.
|
||||
*/
|
||||
export const uploadVideoSegments = async (
|
||||
videoFilePath: string,
|
||||
videoSize: number,
|
||||
objectUploadURL: string,
|
||||
) => {
|
||||
const waitTimeBeforeNextTry = [5000, 20000];
|
||||
|
||||
while (true) {
|
||||
let abort = false;
|
||||
try {
|
||||
const nodeStream = fs_.createReadStream(videoFilePath);
|
||||
const webStream = Readable.toWeb(nodeStream);
|
||||
|
||||
// net.fetch is 40-50x slower than the native fetch for this
|
||||
// particular PUT request. This is easily reproducible (replace
|
||||
// `fetch` with `net.fetch`, then even on localhost the PUT requests
|
||||
// start taking a minute or so; with node's native fetch, it is
|
||||
// second(s)).
|
||||
const res = await fetch(objectUploadURL, {
|
||||
method: "PUT",
|
||||
// net.fetch apparently deduces and inserts a content-length,
|
||||
// because when we use the node native fetch then we need to
|
||||
// provide it explicitly.
|
||||
headers: { "Content-Length": `${videoSize}` },
|
||||
// The duplex option is required since we're passing a stream.
|
||||
//
|
||||
// @ts-expect-error TypeScript's libdom.d.ts does not include
|
||||
// the "duplex" parameter, e.g. see
|
||||
// https://github.com/node-fetch/node-fetch/issues/1769.
|
||||
duplex: "half",
|
||||
body: webStream,
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
// Success.
|
||||
return;
|
||||
}
|
||||
if (res.status >= 400 && res.status < 500) {
|
||||
// HTTP 4xx.
|
||||
abort = true;
|
||||
}
|
||||
throw new Error(
|
||||
`Failed to upload generated HLS video: HTTP ${res.status} ${res.statusText}`,
|
||||
);
|
||||
} catch (e) {
|
||||
if (abort) {
|
||||
throw e;
|
||||
}
|
||||
const t = waitTimeBeforeNextTry.shift();
|
||||
if (!t) {
|
||||
throw e;
|
||||
} else {
|
||||
log.warn("Will retry potentially transient request failure", e);
|
||||
}
|
||||
await wait(t);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ export const messagePortMainEndpoint = (mp: MessagePortMain): Endpoint => {
|
||||
const listeners = new WeakMap<NL, EL>();
|
||||
return {
|
||||
postMessage: (message, transfer) => {
|
||||
mp.postMessage(message, transfer as unknown as MessagePortMain[]);
|
||||
mp.postMessage(message, (transfer ?? []) as MessagePortMain[]);
|
||||
},
|
||||
addEventListener: (_, eh) => {
|
||||
const l: EL = (data) =>
|
||||
|
||||
@@ -15,3 +15,11 @@
|
||||
*/
|
||||
export const wait = (ms: number) =>
|
||||
new Promise((resolve) => setTimeout(resolve, ms));
|
||||
|
||||
/**
|
||||
* Convert `null` to `undefined`, passthrough everything else unchanged.
|
||||
*
|
||||
* Duplicated from `web/packages/utils/transform.ts`.
|
||||
*/
|
||||
export const nullToUndefined = <T>(v: T | null | undefined): T | undefined =>
|
||||
v === null ? undefined : v;
|
||||
|
||||
23
desktop/src/main/utils/exec-worker.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import shellescape from "any-shell-escape";
|
||||
import { exec } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
import log from "../log-worker";
|
||||
|
||||
/**
|
||||
* Run a shell command asynchronously (utility process edition).
|
||||
*
|
||||
* This is an almost verbatim copy of {@link execAsync} from `electron.ts`,
|
||||
* except it is meant to be usable from a utility process where only a subset of
|
||||
* imports are available. See [Note: Using Electron APIs in UtilityProcess].
|
||||
*/
|
||||
export const execAsyncWorker = async (command: string | string[]) => {
|
||||
const escapedCommand = Array.isArray(command)
|
||||
? shellescape(command)
|
||||
: command;
|
||||
const startTime = Date.now();
|
||||
const result = await execAsync_(escapedCommand);
|
||||
log.debugString(`${escapedCommand} (${Date.now() - startTime} ms)`);
|
||||
return result;
|
||||
};
|
||||
|
||||
const execAsync_ = promisify(exec);
|
||||
31
desktop/src/main/utils/http.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
export const clientPackageName = "io.ente.photos.desktop";
|
||||
|
||||
/**
|
||||
* Reimplementation of {@link publicRequestHeaders} from the web source.
|
||||
*
|
||||
* @param desktopAppVersion The desktop app's version. This will get passed on
|
||||
* as the "X-Client-Version" header.
|
||||
*
|
||||
* We cannot directly use `app.getVersion()` to obtain this value since the
|
||||
* {@link app} module is not accessible to Electron utility processes which also
|
||||
* calls this function.
|
||||
*/
|
||||
export const publicRequestHeaders = (desktopAppVersion: string) => ({
|
||||
"X-Client-Package": clientPackageName,
|
||||
"X-Client-Version": desktopAppVersion,
|
||||
});
|
||||
|
||||
/**
|
||||
* Reimplementation of {@link authenticatedRequestHeaders} from the web source.
|
||||
*
|
||||
* This builds on top of {@link publicRequestHeaders} and takes the same
|
||||
* parameters, and additionally also requires the {@link authToken} that will be
|
||||
* passed as the "X-Auth-Token" header.
|
||||
*/
|
||||
export const authenticatedRequestHeaders = (
|
||||
desktopAppVersion: string,
|
||||
authToken: string,
|
||||
) => ({
|
||||
...publicRequestHeaders(desktopAppVersion),
|
||||
"X-Auth-Token": authToken,
|
||||
});
|
||||
@@ -80,8 +80,8 @@ export const deleteTempFileIgnoringErrors = async (tempFilePath: string) => {
|
||||
}
|
||||
};
|
||||
|
||||
/** The result of {@link makeFileForDataOrStreamOrPathOrZipItem}. */
|
||||
interface FileForDataOrPathOrZipItem {
|
||||
/** The result of {@link makeFileForStreamOrPathOrZipItem}. */
|
||||
interface FileForStreamOrPathOrZipItem {
|
||||
/**
|
||||
* The path to the file (possibly temporary).
|
||||
*/
|
||||
@@ -107,13 +107,13 @@ interface FileForDataOrPathOrZipItem {
|
||||
* that needs to be deleted after processing, and a function to write the given
|
||||
* {@link item} into that temporary file if needed.
|
||||
*
|
||||
* @param item The contents of the file (bytes), or a {@link ReadableStream}
|
||||
* with the contents of the file, or the path to an existing file, or a (path to
|
||||
* a zip file, name of an entry within that zip file) tuple.
|
||||
* @param item A {@link ReadableStream} with the contents of the file, or the
|
||||
* path to an existing file, or a (path to a zip file, name of an entry within
|
||||
* that zip file) tuple.
|
||||
*/
|
||||
export const makeFileForDataOrStreamOrPathOrZipItem = async (
|
||||
item: Uint8Array | ReadableStream | string | ZipItem,
|
||||
): Promise<FileForDataOrPathOrZipItem> => {
|
||||
export const makeFileForStreamOrPathOrZipItem = async (
|
||||
item: ReadableStream | string | ZipItem,
|
||||
): Promise<FileForStreamOrPathOrZipItem> => {
|
||||
let path: string;
|
||||
let isFileTemporary: boolean;
|
||||
let writeToTemporaryFile = async () => {
|
||||
@@ -126,9 +126,7 @@ export const makeFileForDataOrStreamOrPathOrZipItem = async (
|
||||
} else {
|
||||
path = await makeTempFilePath();
|
||||
isFileTemporary = true;
|
||||
if (item instanceof Uint8Array) {
|
||||
writeToTemporaryFile = () => fs.writeFile(path, item);
|
||||
} else if (item instanceof ReadableStream) {
|
||||
if (item instanceof ReadableStream) {
|
||||
writeToTemporaryFile = () => writeStream(path, item);
|
||||
} else {
|
||||
writeToTemporaryFile = async () => {
|
||||
|
||||
@@ -193,29 +193,32 @@ const convertToJPEG = (imageData: Uint8Array) =>
|
||||
ipcRenderer.invoke("convertToJPEG", imageData);
|
||||
|
||||
const generateImageThumbnail = (
|
||||
dataOrPathOrZipItem: Uint8Array | string | ZipItem,
|
||||
pathOrZipItem: string | ZipItem,
|
||||
maxDimension: number,
|
||||
maxSize: number,
|
||||
) =>
|
||||
ipcRenderer.invoke(
|
||||
"generateImageThumbnail",
|
||||
dataOrPathOrZipItem,
|
||||
pathOrZipItem,
|
||||
maxDimension,
|
||||
maxSize,
|
||||
);
|
||||
|
||||
const ffmpegExec = (
|
||||
command: FFmpegCommand,
|
||||
dataOrPathOrZipItem: Uint8Array | string | ZipItem,
|
||||
pathOrZipItem: string | ZipItem,
|
||||
outputFileExtension: string,
|
||||
) =>
|
||||
ipcRenderer.invoke(
|
||||
"ffmpegExec",
|
||||
command,
|
||||
dataOrPathOrZipItem,
|
||||
pathOrZipItem,
|
||||
outputFileExtension,
|
||||
);
|
||||
|
||||
const ffmpegDetermineVideoDuration = (pathOrZipItem: string | ZipItem) =>
|
||||
ipcRenderer.invoke("ffmpegDetermineVideoDuration", pathOrZipItem);
|
||||
|
||||
// - Utility processes
|
||||
|
||||
const triggerCreateUtilityProcess = (type: UtilityProcessType) => {
|
||||
@@ -392,6 +395,7 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
convertToJPEG,
|
||||
generateImageThumbnail,
|
||||
ffmpegExec,
|
||||
ffmpegDetermineVideoDuration,
|
||||
|
||||
// - ML
|
||||
|
||||
|
||||
@@ -136,13 +136,20 @@
|
||||
minimatch "^9.0.3"
|
||||
plist "^3.1.0"
|
||||
|
||||
"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0":
|
||||
"@eslint-community/eslint-utils@^4.2.0":
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
|
||||
integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
|
||||
dependencies:
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
"@eslint-community/eslint-utils@^4.7.0":
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz#607084630c6c033992a082de6e6fbc1a8b52175a"
|
||||
integrity sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==
|
||||
dependencies:
|
||||
eslint-visitor-keys "^3.4.3"
|
||||
|
||||
"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.11.0":
|
||||
version "4.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae"
|
||||
@@ -177,10 +184,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.9.1.tgz#4a97e85e982099d6c7ee8410aacb55adaa576f06"
|
||||
integrity sha512-xIDQRsfg5hNBqHz04H1R3scSVwmI+KUbqjsQKHKQ1DAUSaUjYPReZZmS/5PNiKu1fUvzDd6H7DEDKACSEhu+TQ==
|
||||
|
||||
"@eslint/js@^9.25.1":
|
||||
version "9.25.1"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.25.1.tgz#25f5c930c2b68b5ebe7ac857f754cbd61ef6d117"
|
||||
integrity sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==
|
||||
"@eslint/js@^9.27.0":
|
||||
version "9.27.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.27.0.tgz#181a23460877c484f6dd03890f4e3fa2fdeb8ff0"
|
||||
integrity sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==
|
||||
|
||||
"@eslint/object-schema@^2.1.4":
|
||||
version "2.1.4"
|
||||
@@ -268,10 +275,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
||||
integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==
|
||||
|
||||
"@pkgr/core@^0.1.0":
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31"
|
||||
integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==
|
||||
"@pkgr/core@^0.2.4":
|
||||
version "0.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.2.4.tgz#d897170a2b0ba51f78a099edccd968f7b103387c"
|
||||
integrity sha512-ROFF39F6ZrnzSUEmQQZUar0Jt4xVoP9WnDRdWwF4NNcXs3xBTLgBUDoOwW141y1jP+S8nahIbdxbFC7IShw9Iw==
|
||||
|
||||
"@sindresorhus/is@^4.0.0":
|
||||
version "4.6.0"
|
||||
@@ -290,10 +297,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
|
||||
integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
|
||||
|
||||
"@tsconfig/node22@^22.0.1":
|
||||
version "22.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node22/-/node22-22.0.1.tgz#27e3ee9b359e31e5b94690bf2bad5a923c1d57d0"
|
||||
integrity sha512-VkgOa3n6jvs1p+r3DiwBqeEwGAwEvnVCg/hIjiANl5IEcqP3G0u5m8cBJspe1t9qjZRlZ7WFgqq5bJrGdgAKMg==
|
||||
"@tsconfig/node22@^22.0.2":
|
||||
version "22.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node22/-/node22-22.0.2.tgz#1e04e2c5cc946dac787d69bb502462a851ae51b6"
|
||||
integrity sha512-Kmwj4u8sDRDrMYRoN9FDEcXD8UpBSaPQQ24Gz+Gamqfm7xxn+GBR7ge/Z7pK8OXNGyUzbSwJj+TH6B+DS/epyA==
|
||||
|
||||
"@types/auto-launch@^5.0.5":
|
||||
version "5.0.5"
|
||||
@@ -385,85 +392,85 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@8.31.1":
|
||||
version "8.31.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.1.tgz#62f1befe59647524994e89de4516d8dcba7a850a"
|
||||
integrity sha512-oUlH4h1ABavI4F0Xnl8/fOtML/eu8nI2A1nYd+f+55XI0BLu+RIqKoCiZKNo6DtqZBEQm5aNKA20G3Z5w3R6GQ==
|
||||
"@typescript-eslint/eslint-plugin@8.32.1":
|
||||
version "8.32.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz#9185b3eaa3b083d8318910e12d56c68b3c4f45b4"
|
||||
integrity sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==
|
||||
dependencies:
|
||||
"@eslint-community/regexpp" "^4.10.0"
|
||||
"@typescript-eslint/scope-manager" "8.31.1"
|
||||
"@typescript-eslint/type-utils" "8.31.1"
|
||||
"@typescript-eslint/utils" "8.31.1"
|
||||
"@typescript-eslint/visitor-keys" "8.31.1"
|
||||
"@typescript-eslint/scope-manager" "8.32.1"
|
||||
"@typescript-eslint/type-utils" "8.32.1"
|
||||
"@typescript-eslint/utils" "8.32.1"
|
||||
"@typescript-eslint/visitor-keys" "8.32.1"
|
||||
graphemer "^1.4.0"
|
||||
ignore "^5.3.1"
|
||||
ignore "^7.0.0"
|
||||
natural-compare "^1.4.0"
|
||||
ts-api-utils "^2.0.1"
|
||||
ts-api-utils "^2.1.0"
|
||||
|
||||
"@typescript-eslint/parser@8.31.1":
|
||||
version "8.31.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.31.1.tgz#e9b0ccf30d37dde724ee4d15f4dbc195995cce1b"
|
||||
integrity sha512-oU/OtYVydhXnumd0BobL9rkJg7wFJ9bFFPmSmB/bf/XWN85hlViji59ko6bSKBXyseT9V8l+CN1nwmlbiN0G7Q==
|
||||
"@typescript-eslint/parser@8.32.1":
|
||||
version "8.32.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.32.1.tgz#18b0e53315e0bc22b2619d398ae49a968370935e"
|
||||
integrity sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "8.31.1"
|
||||
"@typescript-eslint/types" "8.31.1"
|
||||
"@typescript-eslint/typescript-estree" "8.31.1"
|
||||
"@typescript-eslint/visitor-keys" "8.31.1"
|
||||
"@typescript-eslint/scope-manager" "8.32.1"
|
||||
"@typescript-eslint/types" "8.32.1"
|
||||
"@typescript-eslint/typescript-estree" "8.32.1"
|
||||
"@typescript-eslint/visitor-keys" "8.32.1"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/scope-manager@8.31.1":
|
||||
version "8.31.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.31.1.tgz#1eb52e76878f545e4add142e0d8e3e97e7aa443b"
|
||||
integrity sha512-BMNLOElPxrtNQMIsFHE+3P0Yf1z0dJqV9zLdDxN/xLlWMlXK/ApEsVEKzpizg9oal8bAT5Sc7+ocal7AC1HCVw==
|
||||
"@typescript-eslint/scope-manager@8.32.1":
|
||||
version "8.32.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz#9a6bf5fb2c5380e14fe9d38ccac6e4bbe17e8afc"
|
||||
integrity sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.31.1"
|
||||
"@typescript-eslint/visitor-keys" "8.31.1"
|
||||
"@typescript-eslint/types" "8.32.1"
|
||||
"@typescript-eslint/visitor-keys" "8.32.1"
|
||||
|
||||
"@typescript-eslint/type-utils@8.31.1":
|
||||
version "8.31.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.31.1.tgz#be0f438fb24b03568e282a0aed85f776409f970c"
|
||||
integrity sha512-fNaT/m9n0+dpSp8G/iOQ05GoHYXbxw81x+yvr7TArTuZuCA6VVKbqWYVZrV5dVagpDTtj/O8k5HBEE/p/HM5LA==
|
||||
"@typescript-eslint/type-utils@8.32.1":
|
||||
version "8.32.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz#b9292a45f69ecdb7db74d1696e57d1a89514d21e"
|
||||
integrity sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==
|
||||
dependencies:
|
||||
"@typescript-eslint/typescript-estree" "8.31.1"
|
||||
"@typescript-eslint/utils" "8.31.1"
|
||||
"@typescript-eslint/typescript-estree" "8.32.1"
|
||||
"@typescript-eslint/utils" "8.32.1"
|
||||
debug "^4.3.4"
|
||||
ts-api-utils "^2.0.1"
|
||||
ts-api-utils "^2.1.0"
|
||||
|
||||
"@typescript-eslint/types@8.31.1":
|
||||
version "8.31.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.31.1.tgz#478ed6f7e8aee1be7b63a60212b6bffe1423b5d4"
|
||||
integrity sha512-SfepaEFUDQYRoA70DD9GtytljBePSj17qPxFHA/h3eg6lPTqGJ5mWOtbXCk1YrVU1cTJRd14nhaXWFu0l2troQ==
|
||||
"@typescript-eslint/types@8.32.1":
|
||||
version "8.32.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.32.1.tgz#b19fe4ac0dc08317bae0ce9ec1168123576c1d4b"
|
||||
integrity sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==
|
||||
|
||||
"@typescript-eslint/typescript-estree@8.31.1":
|
||||
version "8.31.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.1.tgz#37792fe7ef4d3021c7580067c8f1ae66daabacdf"
|
||||
integrity sha512-kaA0ueLe2v7KunYOyWYtlf/QhhZb7+qh4Yw6Ni5kgukMIG+iP773tjgBiLWIXYumWCwEq3nLW+TUywEp8uEeag==
|
||||
"@typescript-eslint/typescript-estree@8.32.1":
|
||||
version "8.32.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz#9023720ca4ecf4f59c275a05b5fed69b1276face"
|
||||
integrity sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.31.1"
|
||||
"@typescript-eslint/visitor-keys" "8.31.1"
|
||||
"@typescript-eslint/types" "8.32.1"
|
||||
"@typescript-eslint/visitor-keys" "8.32.1"
|
||||
debug "^4.3.4"
|
||||
fast-glob "^3.3.2"
|
||||
is-glob "^4.0.3"
|
||||
minimatch "^9.0.4"
|
||||
semver "^7.6.0"
|
||||
ts-api-utils "^2.0.1"
|
||||
ts-api-utils "^2.1.0"
|
||||
|
||||
"@typescript-eslint/utils@8.31.1":
|
||||
version "8.31.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.31.1.tgz#5628ea0393598a0b2f143d0fc6d019f0dee9dd14"
|
||||
integrity sha512-2DSI4SNfF5T4oRveQ4nUrSjUqjMND0nLq9rEkz0gfGr3tg0S5KB6DhwR+WZPCjzkZl3cH+4x2ce3EsL50FubjQ==
|
||||
"@typescript-eslint/utils@8.32.1":
|
||||
version "8.32.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.32.1.tgz#4d6d5d29b9e519e9a85e9a74e9f7bdb58abe9704"
|
||||
integrity sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.4.0"
|
||||
"@typescript-eslint/scope-manager" "8.31.1"
|
||||
"@typescript-eslint/types" "8.31.1"
|
||||
"@typescript-eslint/typescript-estree" "8.31.1"
|
||||
"@eslint-community/eslint-utils" "^4.7.0"
|
||||
"@typescript-eslint/scope-manager" "8.32.1"
|
||||
"@typescript-eslint/types" "8.32.1"
|
||||
"@typescript-eslint/typescript-estree" "8.32.1"
|
||||
|
||||
"@typescript-eslint/visitor-keys@8.31.1":
|
||||
version "8.31.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.1.tgz#6742b0e3ba1e0c1e35bdaf78c03e759eb8dd8e75"
|
||||
integrity sha512-I+/rgqOVBn6f0o7NDTmAPWWC6NuqhV174lfYvAm9fUaWeiefLdux9/YI3/nLugEn9L8fcSi0XmpKi/r5u0nmpw==
|
||||
"@typescript-eslint/visitor-keys@8.32.1":
|
||||
version "8.32.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz#4321395cc55c2eb46036cbbb03e101994d11ddca"
|
||||
integrity sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "8.31.1"
|
||||
"@typescript-eslint/types" "8.32.1"
|
||||
eslint-visitor-keys "^4.2.0"
|
||||
|
||||
"@xmldom/xmldom@^0.8.8":
|
||||
@@ -1007,6 +1014,17 @@ cross-env@^7.0.3:
|
||||
dependencies:
|
||||
cross-spawn "^7.0.1"
|
||||
|
||||
cross-spawn@^6.0.0:
|
||||
version "6.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.6.tgz#30d0efa0712ddb7eb5a76e1e8721bffafa6b5d57"
|
||||
integrity sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==
|
||||
dependencies:
|
||||
nice-try "^1.0.4"
|
||||
path-key "^2.0.1"
|
||||
semver "^5.5.0"
|
||||
shebang-command "^1.2.0"
|
||||
which "^1.2.9"
|
||||
|
||||
cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.6:
|
||||
version "7.0.6"
|
||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f"
|
||||
@@ -1087,7 +1105,7 @@ detect-libc@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.3.tgz#f0cd503b40f9939b894697d19ad50895e30cf700"
|
||||
integrity sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==
|
||||
|
||||
detect-newline@^4.0.0:
|
||||
detect-newline@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-4.0.1.tgz#fcefdb5713e1fb8cb2839b8b6ee22e6716ab8f23"
|
||||
integrity sha512-qE3Veg1YXzGHQhlA6jzebZN2qVf6NX+A7m7qlhCGG30dJixrAQhYOsJjsnBjJkCSmuOPpCk30145fr8FV0bzog==
|
||||
@@ -1216,10 +1234,10 @@ electron-updater@^6.6.3:
|
||||
semver "^7.6.3"
|
||||
tiny-typed-emitter "^2.1.0"
|
||||
|
||||
electron@^36.1.0:
|
||||
version "36.1.0"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-36.1.0.tgz#9919b77e61cd1400acc6dd24f9db8451fba5f8eb"
|
||||
integrity sha512-gnp3BnbKdGsVc7cm1qlEaZc8pJsR08mIs8H/yTo8gHEtFkGGJbDTVZOYNAfbQlL0aXh+ozv+CnyiNeDNkT1Upg==
|
||||
electron@^36.3.2:
|
||||
version "36.3.2"
|
||||
resolved "https://registry.yarnpkg.com/electron/-/electron-36.3.2.tgz#4a60f95e8d3858d01570c03b58dc2fb2f17ee8b6"
|
||||
integrity sha512-v0/j7n22CL3OYv9BIhq6JJz2+e1HmY9H4bjTk8/WzVT9JwVX/T/21YNdR7xuQ6XDSEo9gP5JnqmjOamE+CUY8Q==
|
||||
dependencies:
|
||||
"@electron/get" "^2.0.0"
|
||||
"@types/node" "^22.7.7"
|
||||
@@ -1289,7 +1307,7 @@ eslint-scope@^8.0.2:
|
||||
esrecurse "^4.3.0"
|
||||
estraverse "^5.2.0"
|
||||
|
||||
eslint-visitor-keys@^3.3.0:
|
||||
eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.3:
|
||||
version "3.4.3"
|
||||
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
|
||||
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
|
||||
@@ -1372,6 +1390,19 @@ esutils@^2.0.2:
|
||||
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
|
||||
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
|
||||
|
||||
execa@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
|
||||
integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
|
||||
dependencies:
|
||||
cross-spawn "^6.0.0"
|
||||
get-stream "^4.0.0"
|
||||
is-stream "^1.1.0"
|
||||
npm-run-path "^2.0.0"
|
||||
p-finally "^1.0.0"
|
||||
signal-exit "^3.0.0"
|
||||
strip-eof "^1.0.0"
|
||||
|
||||
exponential-backoff@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6"
|
||||
@@ -1438,10 +1469,10 @@ fd-slicer@~1.1.0:
|
||||
dependencies:
|
||||
pend "~1.2.0"
|
||||
|
||||
fdir@^6.4.2:
|
||||
version "6.4.2"
|
||||
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.2.tgz#ddaa7ce1831b161bc3657bb99cb36e1622702689"
|
||||
integrity sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==
|
||||
fdir@^6.4.4:
|
||||
version "6.4.4"
|
||||
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.4.tgz#1cfcf86f875a883e19a8fab53622cfe992e8d2f9"
|
||||
integrity sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==
|
||||
|
||||
ffmpeg-static@^5.2.0:
|
||||
version "5.2.0"
|
||||
@@ -1589,10 +1620,12 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
|
||||
has-symbols "^1.0.3"
|
||||
hasown "^2.0.0"
|
||||
|
||||
get-stdin@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-9.0.0.tgz#3983ff82e03d56f1b2ea0d3e60325f39d703a575"
|
||||
integrity sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==
|
||||
get-stream@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
|
||||
integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
|
||||
dependencies:
|
||||
pump "^3.0.0"
|
||||
|
||||
get-stream@^5.1.0:
|
||||
version "5.2.0"
|
||||
@@ -1601,10 +1634,10 @@ get-stream@^5.1.0:
|
||||
dependencies:
|
||||
pump "^3.0.0"
|
||||
|
||||
git-hooks-list@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/git-hooks-list/-/git-hooks-list-3.1.0.tgz#386dc531dcc17474cf094743ff30987a3d3e70fc"
|
||||
integrity sha512-LF8VeHeR7v+wAbXqfgRlTSX/1BJR9Q1vEMR8JAz1cEg6GX07+zyj3sAdDvYjj/xnlIfVuGgj4qBei1K3hKH+PA==
|
||||
git-hooks-list@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/git-hooks-list/-/git-hooks-list-4.1.1.tgz#ae340b82a9312354c73b48007f33840bbd83d3c0"
|
||||
integrity sha512-cmP497iLq54AZnv4YRAEMnEyQ1eIn4tGKbmswqwmFV4GBnAqE8NLtWxxdXa++AalfgL5EBH4IxTPyquEuGY/jA==
|
||||
|
||||
glob-parent@^5.1.2:
|
||||
version "5.1.2"
|
||||
@@ -1632,7 +1665,7 @@ glob@^10.3.12, glob@^10.3.7:
|
||||
package-json-from-dist "^1.0.0"
|
||||
path-scurry "^1.11.1"
|
||||
|
||||
glob@^7.0.0, glob@^7.1.3, glob@^7.1.6:
|
||||
glob@^7.1.3, glob@^7.1.6:
|
||||
version "7.2.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
|
||||
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
||||
@@ -1830,11 +1863,16 @@ ieee754@^1.1.13:
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||
|
||||
ignore@^5.2.0, ignore@^5.3.1:
|
||||
ignore@^5.2.0:
|
||||
version "5.3.2"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5"
|
||||
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
|
||||
|
||||
ignore@^7.0.0:
|
||||
version "7.0.4"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.4.tgz#a12c70d0f2607c5bf508fb65a40c75f037d7a078"
|
||||
integrity sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==
|
||||
|
||||
import-fresh@^3.2.1:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
|
||||
@@ -1945,6 +1983,11 @@ is-plain-obj@^4.1.0:
|
||||
resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0"
|
||||
integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==
|
||||
|
||||
is-stream@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
|
||||
integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==
|
||||
|
||||
is-unicode-supported@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
|
||||
@@ -2244,7 +2287,7 @@ minimatch@^9.0.3, minimatch@^9.0.4:
|
||||
dependencies:
|
||||
brace-expansion "^2.0.1"
|
||||
|
||||
minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6:
|
||||
minimist@^1.2.5, minimist@^1.2.6, minimist@^1.2.8:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
|
||||
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
|
||||
@@ -2363,6 +2406,11 @@ next-electron-server@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/next-electron-server/-/next-electron-server-1.0.0.tgz#03e133ed64a5ef671b6c6409f908c4901b1828cb"
|
||||
integrity sha512-fTUaHwT0Jry2fbdUSIkAiIqgDAInI5BJFF4/j90/okvZCYlyx6yxpXB30KpzmOG6TN/ESwyvsFJVvS2WHT8PAA==
|
||||
|
||||
nice-try@^1.0.4:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||
|
||||
node-abi@^3.45.0:
|
||||
version "3.67.0"
|
||||
resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.67.0.tgz#1d159907f18d18e18809dbbb5df47ed2426a08df"
|
||||
@@ -2399,6 +2447,13 @@ normalize-url@^6.0.1:
|
||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
|
||||
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
|
||||
|
||||
npm-run-path@^2.0.0:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
|
||||
integrity sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==
|
||||
dependencies:
|
||||
path-key "^2.0.0"
|
||||
|
||||
object-keys@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
||||
@@ -2463,6 +2518,11 @@ p-cancelable@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf"
|
||||
integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==
|
||||
|
||||
p-finally@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
|
||||
integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==
|
||||
|
||||
p-limit@^2.0.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
|
||||
@@ -2535,6 +2595,11 @@ path-is-absolute@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
|
||||
|
||||
path-key@^2.0.0, path-key@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
|
||||
integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==
|
||||
|
||||
path-key@^3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
|
||||
@@ -2599,13 +2664,13 @@ prettier-plugin-organize-imports@^4.1.0:
|
||||
resolved "https://registry.yarnpkg.com/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-4.1.0.tgz#f3d3764046a8e7ba6491431158b9be6ffd83b90f"
|
||||
integrity sha512-5aWRdCgv645xaa58X8lOxzZoiHAldAPChljr/MT0crXVOWTZ+Svl4hIWlz+niYSlO6ikE5UXkN1JrRvIP2ut0A==
|
||||
|
||||
prettier-plugin-packagejson@^2.5.10:
|
||||
version "2.5.10"
|
||||
resolved "https://registry.yarnpkg.com/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.10.tgz#f47068d0aa12efcdddb802189d8adae874ba00e7"
|
||||
integrity sha512-LUxATI5YsImIVSaaLJlJ3aE6wTD+nvots18U3GuQMJpUyClChaZlQrqx3dBnbhF20OnKWZyx8EgyZypQtBDtgQ==
|
||||
prettier-plugin-packagejson@^2.5.14:
|
||||
version "2.5.14"
|
||||
resolved "https://registry.yarnpkg.com/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.5.14.tgz#8ada09114ff60c7f42c3f8755ffb2f8152f3624f"
|
||||
integrity sha512-h+3tSpr2nVpp+YOK1MDIYtYhHVXr8/0V59UUbJpIJFaqi3w4fvUokJo6eV8W+vELrUXIZzJ+DKm5G7lYzrMcKQ==
|
||||
dependencies:
|
||||
sort-package-json "2.15.1"
|
||||
synckit "0.9.2"
|
||||
sort-package-json "3.2.1"
|
||||
synckit "0.11.6"
|
||||
|
||||
prettier@3.5.3:
|
||||
version "3.5.3"
|
||||
@@ -2829,6 +2894,11 @@ semver@^7.3.2, semver@^7.3.5, semver@^7.3.8, semver@^7.5.3, semver@^7.6.0, semve
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143"
|
||||
integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==
|
||||
|
||||
semver@^7.7.1:
|
||||
version "7.7.2"
|
||||
resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58"
|
||||
integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
|
||||
|
||||
serialize-error@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18"
|
||||
@@ -2836,6 +2906,13 @@ serialize-error@^7.0.1:
|
||||
dependencies:
|
||||
type-fest "^0.13.1"
|
||||
|
||||
shebang-command@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
|
||||
integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==
|
||||
dependencies:
|
||||
shebang-regex "^1.0.0"
|
||||
|
||||
shebang-command@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
|
||||
@@ -2843,6 +2920,11 @@ shebang-command@^2.0.0:
|
||||
dependencies:
|
||||
shebang-regex "^3.0.0"
|
||||
|
||||
shebang-regex@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
|
||||
integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==
|
||||
|
||||
shebang-regex@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
|
||||
@@ -2853,24 +2935,25 @@ shell-quote@^1.8.1:
|
||||
resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680"
|
||||
integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==
|
||||
|
||||
shelljs@^0.8.5:
|
||||
version "0.8.5"
|
||||
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c"
|
||||
integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==
|
||||
shelljs@^0.9.2:
|
||||
version "0.9.2"
|
||||
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.9.2.tgz#a8ac724434520cd7ae24d52071e37a18ac2bb183"
|
||||
integrity sha512-S3I64fEiKgTZzKCC46zT/Ib9meqofLrQVbpSswtjFfAVDW+AZ54WTnAM/3/yENoxz/V1Cy6u3kiiEbQ4DNphvw==
|
||||
dependencies:
|
||||
glob "^7.0.0"
|
||||
execa "^1.0.0"
|
||||
fast-glob "^3.3.2"
|
||||
interpret "^1.0.0"
|
||||
rechoir "^0.6.2"
|
||||
|
||||
shx@^0.3.4:
|
||||
version "0.3.4"
|
||||
resolved "https://registry.yarnpkg.com/shx/-/shx-0.3.4.tgz#74289230b4b663979167f94e1935901406e40f02"
|
||||
integrity sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==
|
||||
shx@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/shx/-/shx-0.4.0.tgz#c6ea6ace7e778da0ab32d2eab9def59d788e9336"
|
||||
integrity sha512-Z0KixSIlGPpijKgcH6oCMCbltPImvaKy0sGH8AkLRXw1KyzpKtaCTizP2xen+hNDqVF4xxgvA0KXSb9o4Q6hnA==
|
||||
dependencies:
|
||||
minimist "^1.2.3"
|
||||
shelljs "^0.8.5"
|
||||
minimist "^1.2.8"
|
||||
shelljs "^0.9.2"
|
||||
|
||||
signal-exit@^3.0.2:
|
||||
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||
version "3.0.7"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
|
||||
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
|
||||
@@ -2923,19 +3006,18 @@ sort-object-keys@^1.1.3:
|
||||
resolved "https://registry.yarnpkg.com/sort-object-keys/-/sort-object-keys-1.1.3.tgz#bff833fe85cab147b34742e45863453c1e190b45"
|
||||
integrity sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==
|
||||
|
||||
sort-package-json@2.15.1:
|
||||
version "2.15.1"
|
||||
resolved "https://registry.yarnpkg.com/sort-package-json/-/sort-package-json-2.15.1.tgz#e5a035fad7da277b1947b9eecc93ea09c1c2526e"
|
||||
integrity sha512-9x9+o8krTT2saA9liI4BljNjwAbvUnWf11Wq+i/iZt8nl2UGYnf3TH5uBydE7VALmP7AGwlfszuEeL8BDyb0YA==
|
||||
sort-package-json@3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/sort-package-json/-/sort-package-json-3.2.1.tgz#889f3bdf43ceeff5fa4278a7c53ae5b1520d287e"
|
||||
integrity sha512-rTfRdb20vuoAn7LDlEtCqOkYfl2X+Qze6cLbNOzcDpbmKEhJI30tTN44d5shbKJnXsvz24QQhlCm81Bag7EOKg==
|
||||
dependencies:
|
||||
detect-indent "^7.0.1"
|
||||
detect-newline "^4.0.0"
|
||||
get-stdin "^9.0.0"
|
||||
git-hooks-list "^3.0.0"
|
||||
detect-newline "^4.0.1"
|
||||
git-hooks-list "^4.0.0"
|
||||
is-plain-obj "^4.1.0"
|
||||
semver "^7.6.0"
|
||||
semver "^7.7.1"
|
||||
sort-object-keys "^1.1.3"
|
||||
tinyglobby "^0.2.9"
|
||||
tinyglobby "^0.2.12"
|
||||
|
||||
source-map-support@^0.5.19:
|
||||
version "0.5.21"
|
||||
@@ -2990,6 +3072,11 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
|
||||
dependencies:
|
||||
ansi-regex "^5.0.1"
|
||||
|
||||
strip-eof@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
|
||||
integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==
|
||||
|
||||
strip-json-comments@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||
@@ -3021,13 +3108,12 @@ supports-preserve-symlinks-flag@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
synckit@0.9.2:
|
||||
version "0.9.2"
|
||||
resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.9.2.tgz#a3a935eca7922d48b9e7d6c61822ee6c3ae4ec62"
|
||||
integrity sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==
|
||||
synckit@0.11.6:
|
||||
version "0.11.6"
|
||||
resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.11.6.tgz#e742a0c27bbc1fbc96f2010770521015cca7ed5c"
|
||||
integrity sha512-2pR2ubZSV64f/vqm9eLPz/KOvR9Dm+Co/5ChLgeHl0yEDRc6h5hXHoxEQH8Y5Ljycozd3p1k5TTSVdzYGkPvLw==
|
||||
dependencies:
|
||||
"@pkgr/core" "^0.1.0"
|
||||
tslib "^2.6.2"
|
||||
"@pkgr/core" "^0.2.4"
|
||||
|
||||
tar@^6.0.5, tar@^6.1.11, tar@^6.1.12, tar@^6.2.1:
|
||||
version "6.2.1"
|
||||
@@ -3078,12 +3164,12 @@ tiny-typed-emitter@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz#b3b027fdd389ff81a152c8e847ee2f5be9fad7b5"
|
||||
integrity sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==
|
||||
|
||||
tinyglobby@^0.2.9:
|
||||
version "0.2.10"
|
||||
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.10.tgz#e712cf2dc9b95a1f5c5bbd159720e15833977a0f"
|
||||
integrity sha512-Zc+8eJlFMvgatPZTl6A9L/yht8QqdmUNtURHaKZLmKBE12hNPSrqNkUp2cs3M/UKmNVVAMFQYSjYIVHDjW5zew==
|
||||
tinyglobby@^0.2.12:
|
||||
version "0.2.13"
|
||||
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.13.tgz#a0e46515ce6cbcd65331537e57484af5a7b2ff7e"
|
||||
integrity sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==
|
||||
dependencies:
|
||||
fdir "^6.4.2"
|
||||
fdir "^6.4.4"
|
||||
picomatch "^4.0.2"
|
||||
|
||||
tmp-promise@^3.0.2:
|
||||
@@ -3117,12 +3203,12 @@ truncate-utf8-bytes@^1.0.0:
|
||||
dependencies:
|
||||
utf8-byte-length "^1.0.1"
|
||||
|
||||
ts-api-utils@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.0.1.tgz#660729385b625b939aaa58054f45c058f33f10cd"
|
||||
integrity sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==
|
||||
ts-api-utils@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz#595f7094e46eed364c13fd23e75f9513d29baf91"
|
||||
integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==
|
||||
|
||||
tslib@^2.1.0, tslib@^2.6.2:
|
||||
tslib@^2.1.0:
|
||||
version "2.7.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01"
|
||||
integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==
|
||||
@@ -3149,14 +3235,14 @@ typedarray@^0.0.6:
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==
|
||||
|
||||
typescript-eslint@^8.31.1:
|
||||
version "8.31.1"
|
||||
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.31.1.tgz#b77ab1e48ced2daab9225ff94bab54391a4af69b"
|
||||
integrity sha512-j6DsEotD/fH39qKzXTQRwYYWlt7D+0HmfpOK+DVhwJOFLcdmn92hq3mBb7HlKJHbjjI/gTOqEcc9d6JfpFf/VA==
|
||||
typescript-eslint@^8.32.1:
|
||||
version "8.32.1"
|
||||
resolved "https://registry.yarnpkg.com/typescript-eslint/-/typescript-eslint-8.32.1.tgz#1784335c781491be528ff84ab666e2f0f7591fd1"
|
||||
integrity sha512-D7el+eaDHAmXvrZBy1zpzSNIRqnCOrkwTgZxTu3MUqRWk8k0q9m9Ho4+vPf7iHtgUfrK/o8IZaEApsxPlHTFCg==
|
||||
dependencies:
|
||||
"@typescript-eslint/eslint-plugin" "8.31.1"
|
||||
"@typescript-eslint/parser" "8.31.1"
|
||||
"@typescript-eslint/utils" "8.31.1"
|
||||
"@typescript-eslint/eslint-plugin" "8.32.1"
|
||||
"@typescript-eslint/parser" "8.32.1"
|
||||
"@typescript-eslint/utils" "8.32.1"
|
||||
|
||||
typescript@^5.4.3, typescript@^5.8.3:
|
||||
version "5.8.3"
|
||||
@@ -3230,6 +3316,13 @@ wcwidth@^1.0.1:
|
||||
dependencies:
|
||||
defaults "^1.0.3"
|
||||
|
||||
which@^1.2.9:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
|
||||
dependencies:
|
||||
isexe "^2.0.0"
|
||||
|
||||
which@^2.0.1, which@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
|
||||
@@ -3311,3 +3404,8 @@ yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
|
||||
|
||||
zod@^3.25.23:
|
||||
version "3.25.23"
|
||||
resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.23.tgz#128fb02f3619a8bca6bbbf6b07b457236cf33391"
|
||||
integrity sha512-Od2bdMosahjSrSgJtakrwjMDb1zM1A3VIHCPGveZt/3/wlrTWBya2lmEh2OYe4OIu8mPTmmr0gnLHIWQXdtWBg==
|
||||
|
||||
@@ -41,6 +41,16 @@ Usually, this discrepancy occurs because the time in your browser might be
|
||||
incorrect. In particular, multiple users have reported that Firefox provides
|
||||
incorrect time when certain privacy settings are enabled.
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Newer Ente Auth clients (upcoming 4.4.0+) will automatically try to correct
|
||||
> for incorrect system time, so you should be seeing correct codes even if your
|
||||
> system time is out of sync. However, this automatic correction will not work
|
||||
> if you're using Ente Auth in offline mode.
|
||||
>
|
||||
> If you've recently changed your system time and the codes are still incorrect,
|
||||
> try to refresh / restart the app if needed.
|
||||
|
||||
### Can I access my codes on web?
|
||||
|
||||
You can access your codes on the web at [auth.ente.io](https://auth.ente.io).
|
||||
|
||||
@@ -21,7 +21,7 @@ always available on your machine.
|
||||
background without you needing to run any other cron jobs. See
|
||||
[migration/export](/photos/migration/export/) for more details.
|
||||
|
||||
## Does the exported data preserve folder structure?
|
||||
## Does the exported data preserve album structure?
|
||||
|
||||
Yes. When you export your data for local backup, it will maintain the exact
|
||||
album structure how you have set up within Ente.
|
||||
|
||||
@@ -26,9 +26,6 @@ unsupported file format and we will do our best to help you out.
|
||||
|
||||
Yes, we currently do not support files larger than 4 GB.
|
||||
|
||||
If this constraint is a concern for you, please write to
|
||||
[support@ente.io](mailto:support@ente.io) with your use case and we will do our
|
||||
best to help you.
|
||||
|
||||
## Does Ente support videos?
|
||||
|
||||
|
||||
@@ -8,22 +8,43 @@ description:
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> Video streaming is available in beta on mobile apps starting v0.9.98.
|
||||
> Video streaming is available in beta on mobile apps starting v0.9.98 and on
|
||||
> desktop starting v1.7.13.
|
||||
|
||||
### How to enable video streaming?
|
||||
|
||||
#### On mobile
|
||||
|
||||
- Open Settings -> General -> Advanced
|
||||
- Switch on the toggle for `Video streaming`
|
||||
- Enable the toggle for `Streamable videos`
|
||||
|
||||
#### On desktop
|
||||
|
||||
- Open Settings -> Preferences
|
||||
- Enable the toggle for `Streamable videos`
|
||||
|
||||
### What happens when I enable video streaming?
|
||||
|
||||
#### On mobile
|
||||
|
||||
Enabling video streaming will start processing videos captured in the last 30
|
||||
days, generating streams for each. Both local and remote videos will be
|
||||
processed, so this may consume bandwidth for downloading of remote files and
|
||||
uploading of the generated streams.
|
||||
|
||||
#### On desktop
|
||||
|
||||
When enabled, the desktop app will generate streams both for new uploads, and
|
||||
also for all existing videos that were previously uploaded.
|
||||
|
||||
Stream generation is CPU intensive and can take time so the app will continue
|
||||
processing them in the background. Clicking on search bar will show "Processing
|
||||
videos..." when stream generation is happening.
|
||||
|
||||
### How can I view video streams?
|
||||
|
||||
### On mobile
|
||||
|
||||
Settings -> Backup > Backup status will show details regarding the processing
|
||||
status for videos. Processed videos will have a green play button next to them.
|
||||
You can open these videos by tapping on them.
|
||||
@@ -34,6 +55,12 @@ play the stream.
|
||||
Clicking on the `Info` icon within the original video will show details about
|
||||
the generated stream.
|
||||
|
||||
### On desktop and web
|
||||
|
||||
Desktop and web app will automatically play the streaming version of a video if
|
||||
it has been already generated. The quality selector will show "Auto" when
|
||||
playing the stream.
|
||||
|
||||
### What is a stream?
|
||||
|
||||
Stream is an encrypted HLS file with an `.m3u8` playlist that helps play a video
|
||||
|
||||
@@ -45,5 +45,5 @@ Typically,`<host>:<container-port>`
|
||||
1. `8080:8080`: Museum (Ente's server)
|
||||
2. `3000:3000`: Ente Photos web app
|
||||
3. `3001:3001`: Ente Accounts web app
|
||||
4. `3003:3003`: [EEnte Auth](https://ente.io/auth/)
|
||||
4. `3003:3003`: [Ente Auth web app](https://ente.io/auth/)
|
||||
5. `3004:3004`: [Ente Cast web app](http://ente.io/cast)
|
||||
|
||||
@@ -3,32 +3,6 @@ title: Server admin
|
||||
description: Administering your custom self-hosted Ente instance using the CLI
|
||||
---
|
||||
|
||||
# Administering your custom server
|
||||
|
||||
You can use
|
||||
[Ente's CLI](https://github.com/ente-io/ente/releases?q=tag%3Acli-v0) to
|
||||
administer your self hosted server.
|
||||
|
||||
First we need to get your CLI to connect to your custom server. Define a
|
||||
config.yaml and put it either in the same directory as CLI or path defined in
|
||||
env variable `ENTE_CLI_CONFIG_PATH`
|
||||
|
||||
```yaml
|
||||
endpoint:
|
||||
api: "http://localhost:8080"
|
||||
```
|
||||
|
||||
Now you should be able to
|
||||
[add an account](https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente_account_add.md),
|
||||
and subsequently increase the
|
||||
[storage and account validity](https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente_admin_update-subscription.md)
|
||||
using the CLI.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> The CLI command to add an account does not create Ente accounts. It only adds
|
||||
> existing accounts to the list of (existing) accounts that the CLI can use.
|
||||
|
||||
## Becoming an admin
|
||||
|
||||
By default, the first user (and only the first user) created on the system is
|
||||
@@ -40,7 +14,7 @@ explicit whitelist of admins.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> The first user is only treated as the admin if the list of admins in the
|
||||
> The first user is only treated as the admin if the list of admins in the
|
||||
> configuration is empty.
|
||||
>
|
||||
> Also, if at some point you delete the first user, then you will need to define
|
||||
@@ -78,6 +52,38 @@ You can use
|
||||
[account list](https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente_account_list.md)
|
||||
command to find the user id of any account.
|
||||
|
||||
# Administering your custom server
|
||||
|
||||
> [!NOTE]
|
||||
> For the first user (admin) to perform administrative actions using the CLI, their
|
||||
> userID must be whitelisted in the `museum.yaml` configuration file under
|
||||
> `internal.admins`. While the first user is automatically granted admin privileges
|
||||
> on the server, this additional step is required for CLI operations.
|
||||
|
||||
You can use
|
||||
[Ente's CLI](https://github.com/ente-io/ente/releases?q=tag%3Acli-v0) to
|
||||
administer your self hosted server.
|
||||
|
||||
First we need to get your CLI to connect to your custom server. Define a
|
||||
config.yaml and put it either in the same directory as CLI or path defined in
|
||||
env variable `ENTE_CLI_CONFIG_PATH`
|
||||
|
||||
```yaml
|
||||
endpoint:
|
||||
api: "http://localhost:8080"
|
||||
```
|
||||
|
||||
Now you should be able to
|
||||
[add an account](https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente_account_add.md),
|
||||
and subsequently increase the
|
||||
[storage and account validity](https://github.com/ente-io/ente/blob/main/cli/docs/generated/ente_admin_update-subscription.md)
|
||||
using the CLI.
|
||||
|
||||
> [!NOTE]
|
||||
>
|
||||
> The CLI command to add an account does not create Ente accounts. It only adds
|
||||
> existing accounts to the list of (existing) accounts that the CLI can use.
|
||||
|
||||
## Backups
|
||||
|
||||
See this [FAQ](/self-hosting/faq/backup).
|
||||
|
||||
@@ -49,10 +49,10 @@ For example,
|
||||
|
||||
```yaml
|
||||
apps:
|
||||
public-albums: albums.myente.xyz
|
||||
cast: cast.myente.xyz
|
||||
accounts: accounts.myente.xyz
|
||||
family: family.myente.xyz
|
||||
public-albums: https://albums.myente.xyz
|
||||
cast: https://cast.myente.xyz
|
||||
accounts: https://accounts.myente.xyz
|
||||
family: https://family.myente.xyz
|
||||
```
|
||||
|
||||
>[!IMPORTANT]
|
||||
@@ -74,4 +74,4 @@ functionalities like SMTP, Discord notifications, Hardcoded-OTTs, etc.
|
||||
|
||||
## References
|
||||
|
||||
- [Environment variables and ports](/self-hosting/faq/environment)
|
||||
- [Environment variables and ports](/self-hosting/faq/environment)
|
||||
|
||||
@@ -3,14 +3,14 @@ title: Uploads
|
||||
description: Fixing upload errors when trying to self host Ente
|
||||
---
|
||||
|
||||
# Troubleshooting upload failures
|
||||
# Troubleshooting upload failures
|
||||
|
||||
Here are some errors our community members frequently encountered with the
|
||||
context and potential fixes.
|
||||
|
||||
Fundamentally in most situations, the problem is because of minor mistakes or
|
||||
misconfiguration. Please make sure to reverse proxy museum and MinIO API
|
||||
endpoint to a domain and check your S3 credentials and whole configuration
|
||||
misconfiguration. Please make sure to reverse proxy museum and MinIO API
|
||||
endpoint to a domain and check your S3 credentials and whole configuration
|
||||
file for any minor misconfigurations.
|
||||
|
||||
It is also suggested that the user setups bucket CORS on MinIO or any external
|
||||
@@ -21,10 +21,10 @@ this](/self-hosting/troubleshooting/bucket-cors).
|
||||
|
||||
S3 is an cloud storage protocol made by Amazon (specifically AWS). S3 is designed to store
|
||||
files and data as objects inside Buckets and it is mostly used for Online
|
||||
Backups and storing different types of files.
|
||||
Backups and storing different types of files.
|
||||
|
||||
Ente's Docker setup is shipped with [MinIO](https://min.io/) as its default S3 provider.
|
||||
MinIO supports the Amazon S3 protocol and leverages your disk storage to
|
||||
Ente's Docker setup is shipped with [MinIO](https://min.io/) as its default S3 provider.
|
||||
MinIO supports the Amazon S3 protocol and leverages your disk storage to
|
||||
dump all the uploaded files as encrypted object blobs.
|
||||
|
||||
## 403 Forbidden
|
||||
@@ -40,15 +40,15 @@ This could be because
|
||||
|
||||
1. The bucket CORS rules do not allow museum to access these objects. For
|
||||
uploading files from the browser, you will need to set `allowedOrigins` to
|
||||
`*`, and allow the `X-Auth-Token`, `X-Client-Package` headers configuration
|
||||
too. [Here is an example of a working
|
||||
`*`, and allow the `X-Auth-Token`, `X-Client-Package`, `X-Client-Version`
|
||||
headers configuration too. [Here is an example of a working
|
||||
configuration](https://github.com/ente-io/ente/discussions/1764#discussioncomment-9478204).
|
||||
|
||||
2. The credentials are not being picked up (you might be setting the correct
|
||||
credentials, but not in the place where museum reads them from).
|
||||
|
||||
## Mismatch in file size
|
||||
## Mismatch in file size
|
||||
|
||||
The "Mismatch in file size" error mostly occurs in a situation where the client is re-uploading a file which is already in the bucket with a different
|
||||
file size. The reason for re-upload could be anything including network issue,
|
||||
sudden killing of app before the upload is complete and etc.
|
||||
sudden killing of app before the upload is complete and etc.
|
||||
|
||||
@@ -36,7 +36,7 @@ else
|
||||
BACKUP_ID=$(scw rdb backup create instance-id=$SCW_RDB_INSTANCE_ID \
|
||||
name=$BACKUP_NAME expires-at=$EXPIRY \
|
||||
database-name=ente_db -o json | jq -r '.id')
|
||||
scw rdb backup wait $BACKUP_ID timeout=5h
|
||||
scw rdb backup wait $BACKUP_ID timeout=8h
|
||||
scw rdb backup download output=$BACKUP_FILE \
|
||||
$(scw rdb backup export $BACKUP_ID --wait -o json | jq -r '.id')
|
||||
fi
|
||||
|
||||
5
infra/workers/csp-reporter/package.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "csp-reporter",
|
||||
"version": "0.0.0",
|
||||
"private": true
|
||||
}
|
||||
23
infra/workers/csp-reporter/src/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Log CSP reports.
|
||||
*
|
||||
* See _headers in the web app source.
|
||||
*/
|
||||
|
||||
export default {
|
||||
async fetch(request: Request) {
|
||||
switch (request.method) {
|
||||
case "POST":
|
||||
return handlePOST(request);
|
||||
default:
|
||||
console.log(`Unsupported HTTP method ${request.method}`);
|
||||
return new Response(null, { status: 405 });
|
||||
}
|
||||
},
|
||||
} satisfies ExportedHandler;
|
||||
|
||||
const handlePOST = async (request: Request) => {
|
||||
// {job="worker"} |= `[csp-report]` | json log="logs[0]" | keep log
|
||||
console.log("[csp-report]", await request.text());
|
||||
return new Response(null, { status: 200 });
|
||||
};
|
||||
1
infra/workers/csp-reporter/tsconfig.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "extends": "../tsconfig.base.json", "include": ["src"] }
|
||||
9
infra/workers/csp-reporter/wrangler.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
name = "csp-reporter"
|
||||
main = "src/index.ts"
|
||||
compatibility_date = "2025-05-19"
|
||||
|
||||
routes = [
|
||||
{ pattern = "csp-reporter.ente.io", custom_domain = true }
|
||||
]
|
||||
|
||||
tail_consumers = [{ service = "tail" }]
|
||||
@@ -21,7 +21,7 @@ const handleOPTIONS = (request: Request) => {
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "X-Auth-Token, X-Client-Package",
|
||||
"Access-Control-Allow-Headers": "X-Auth-Token, X-Client-Package, X-Client-Version",
|
||||
"Access-Control-Max-Age": "86400",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
"name": "workers",
|
||||
"private": true,
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20240614.0",
|
||||
"typescript": "^5",
|
||||
"wrangler": "^3",
|
||||
"prettier": "^3.3.4"
|
||||
"@cloudflare/workers-types": "^4.20250519.0",
|
||||
"typescript": "^5.8.3",
|
||||
"wrangler": "^4.15.2",
|
||||
"prettier": "^3.5.3"
|
||||
},
|
||||
"workspaces": [
|
||||
"*"
|
||||
|
||||
@@ -22,7 +22,7 @@ const handleOPTIONS = (request: Request) => {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
||||
"Access-Control-Allow-Headers":
|
||||
"X-Auth-Access-Token, X-Auth-Access-Token-JWT, X-Client-Package",
|
||||
"X-Auth-Access-Token, X-Auth-Access-Token-JWT, X-Client-Package, X-Client-Version",
|
||||
"Access-Control-Max-Age": "86400",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -21,7 +21,7 @@ const handleOPTIONS = (request: Request) => {
|
||||
headers: {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
||||
"Access-Control-Allow-Headers": "X-Auth-Token, X-Client-Package",
|
||||
"Access-Control-Allow-Headers": "X-Auth-Token, X-Client-Package, X-Client-Version",
|
||||
"Access-Control-Max-Age": "86400",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@ const handleOPTIONS = (request: Request) => {
|
||||
"Access-Control-Allow-Origin": "*",
|
||||
"Access-Control-Allow-Methods": "POST, PUT, OPTIONS",
|
||||
"Access-Control-Allow-Headers":
|
||||
"Content-Type, UPLOAD-URL, X-Client-Package",
|
||||
"Content-Type, UPLOAD-URL, X-Client-Package, X-Client-Version",
|
||||
"Access-Control-Expose-Headers": "X-Request-Id, CF-Ray",
|
||||
"Access-Control-Max-Age": "86400",
|
||||
},
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:allowBackup="false"
|
||||
android:fullBackupContent="false"
|
||||
android:largeHeap="true">
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:largeHeap="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
>
|
||||
|
||||
<activity android:name=".MainActivity" android:launchMode="singleTask"
|
||||
android:theme="@style/LaunchTheme"
|
||||
@@ -150,6 +153,15 @@
|
||||
<meta-data android:name="android.appwidget.provider"
|
||||
android:resource="@xml/memory_widget" />
|
||||
</receiver>
|
||||
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" /> <!-- Needed for scheduling notifications -->
|
||||
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver"> <!-- Needed for scheduling notifications -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
|
||||
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</application>
|
||||
|
||||
<!-- Android 11: https://developer.android.com/preview/privacy/package-visibility -->
|
||||
@@ -177,4 +189,6 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="com.android.vending.BILLING" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/> <!-- Needed for scheduling notifications -->
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> <!-- Needed for scheduling notifications -->
|
||||
</manifest>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<exclude domain="root" path="."/>
|
||||
<exclude domain="file" path="."/>
|
||||
<exclude domain="database" path="."/>
|
||||
<exclude domain="sharedpref" path="."/>
|
||||
<exclude domain="external" path="."/>
|
||||
</cloud-backup>
|
||||
|
||||
<device-transfer>
|
||||
<exclude domain="root" path="."/>
|
||||
<exclude domain="file" path="."/>
|
||||
<exclude domain="database" path="."/>
|
||||
<exclude domain="sharedpref" path="."/>
|
||||
<exclude domain="external" path="."/>
|
||||
</device-transfer>
|
||||
</data-extraction-rules>
|
||||
@@ -0,0 +1,14 @@
|
||||
<network-security-config>
|
||||
<base-config>
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
<certificates src="user" />
|
||||
</trust-anchors>
|
||||
</base-config>
|
||||
<domain-config cleartextTrafficPermitted="false">
|
||||
<domain includeSubdomains="true">ente.io</domain>
|
||||
<trust-anchors>
|
||||
<certificates src="system" />
|
||||
</trust-anchors>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
@@ -1,4 +1,4 @@
|
||||
ente merupakan app sederhana yang memungkinkanmu untuk mencadangkan serta membagikan foto dan video.
|
||||
ente adalah aplikasi sederhana untuk mencadangkan serta membagikan foto dan video anda.
|
||||
|
||||
Jika kamu ingin alternatif Google Photos yang ramah privasi, kamu menemukan app yang tepat. Dengan ente, kenanganmu terenkripsi dari ujung ke ujung (e2ee). Sehingga, hanya kamu yang dapat melihatnya.
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ ente ayrıca, ente'de olmasalar bile albümlerinizi sevdiklerinizle paylaşmanı
|
||||
- Aile planları, böylece depolama alanını ailenizle paylaşabilirsiniz
|
||||
- Seyahatten sonra fotoğrafları bir araya toplayabilmeniz için ortak albümler
|
||||
- Partnerinizin "Kamera" tıklamalarınızın keyfini çıkarmasını istemeniz durumunda paylaşılan klasörler
|
||||
- Parola ile korunabilen ve süresi dolacak şekilde ayarlanabilen albüm bağlantıları
|
||||
- Albüm bağlantıları, parolayla korunabilir
|
||||
- Güvenli bir şekilde yedeklenmiş dosyaları kaldırarak alan boşaltma yeteneği
|
||||
- İnsan desteği, çünkü sen buna değersin
|
||||
- Açıklamalar, böylece anılarınıza başlık yazabilir ve onları kolayca bulabilirsiniz
|
||||
@@ -27,10 +27,10 @@ ente ayrıca, ente'de olmasalar bile albümlerinizi sevdiklerinizle paylaşmanı
|
||||
- ve çok daha fazlası!
|
||||
|
||||
İZİNLER
|
||||
bir fotoğraf depolama sağlayıcısının amacına hizmet etmek için belirli izinlere yönelik taleplerde bulunulabilir; bu izinler burada incelenebilir: https://github.com/ente-io/ente/blob/f-droid/mobile/android/permissions.md
|
||||
ente, burada gözden geçirilebilecek bir fotoğraf depolama sağlayıcısının amacına hizmet etmek için belirli izinler ister: https://github.com/ente-io/ente/blob/f-droid/mobile/android/permissions.md
|
||||
|
||||
FİYATLANDIRMA
|
||||
Sonsuza kadar ücretsiz planlar sunmuyoruz, çünkü sürdürülebilir kalmamız ve zamanın testine dayanmamız bizim için önemli. Bunun yerine, ailenizle özgürce paylaşabileceğiniz uygun fiyatlı planlar sunuyoruz. Daha fazla bilgiyi ente.io adresinde bulabilirsiniz.
|
||||
|
||||
🙋DESTEK
|
||||
DESTEK
|
||||
İnsan desteği sunmaktan gurur duyuyoruz. Ücretli müşterimiz iseniz team@ente.io adresine ulaşabilir ve ekibimizden 24 saat içinde yanıt bekleyebilirsiniz.
|
||||
|
||||
@@ -14,7 +14,7 @@ Ente, albümlerinizi sevdiklerinizle paylaşmanızı da kolaylaştırıyor. Bunl
|
||||
- Orijinal kalitede yedekler, çünkü her piksel önemlidir
|
||||
- Aile planları, böylece depolama alanını ailenizle paylaşabilirsiniz
|
||||
- Partnerinizin "Kamera" tıklamalarınızın keyfini çıkarmasını istemeniz durumunda paylaşılan klasörler
|
||||
- Parola ile korunabilen ve süresi dolacak şekilde ayarlanabilen albüm bağlantıları
|
||||
- Albüm bağlantıları, parolayla korunabilir ve son kullanma tarihi ayarlanabilir
|
||||
- Güvenli bir şekilde yedeklenmiş dosyaları kaldırarak alan boşaltma yeteneği
|
||||
- Son rötuşları eklemek için görüntü düzenleyici
|
||||
- Favori, sakla ve anılarını yeniden yaşa, çünkü onlar değerlidir
|
||||
@@ -26,7 +26,7 @@ Ente, albümlerinizi sevdiklerinizle paylaşmanızı da kolaylaştırıyor. Bunl
|
||||
FİYATLANDIRMA
|
||||
Sonsuza kadar ücretsiz planlar sunmuyoruz, çünkü sürdürülebilir kalmamız ve zamanın testine dayanmamız bizim için önemli. Bunun yerine, ailenizle özgürce paylaşabileceğiniz uygun fiyatlı planlar sunuyoruz. Daha fazla bilgiyi ente.io adresinde bulabilirsiniz.
|
||||
|
||||
🙋DESTEK
|
||||
DESTEK
|
||||
İnsan desteği sunmaktan gurur duyuyoruz. Ücretli müşterimiz iseniz team@ente.io adresine ulaşabilir ve ekibimizden 24 saat içinde yanıt bekleyebilirsiniz.
|
||||
|
||||
ŞARTLAR
|
||||
|
||||
@@ -1 +1 @@
|
||||
Ente - şifrelenmiş depolama sistemi
|
||||
Ente şifrelenmiş depolama sistemi
|
||||
|
||||
@@ -14,7 +14,7 @@ Ente, albümlerinizi sevdiklerinizle paylaşmanızı da kolaylaştırıyor. Bunl
|
||||
- Orijinal kalitede yedekler, çünkü her piksel önemlidir
|
||||
- Aile planları, böylece depolama alanını ailenizle paylaşabilirsiniz
|
||||
- Partnerinizin "Kamera" tıklamalarınızın keyfini çıkarmasını istemeniz durumunda paylaşılan klasörler
|
||||
- Parola ile korunabilen ve süresi dolacak şekilde ayarlanabilen albüm bağlantıları
|
||||
- Albüm bağlantıları, parolayla korunabilir ve son kullanma tarihi ayarlanabilir
|
||||
- Güvenli bir şekilde yedeklenmiş dosyaları kaldırarak alan boşaltma yeteneği
|
||||
- Son rötuşları eklemek için görüntü düzenleyici
|
||||
- Favori, sakla ve anılarını yeniden yaşa, çünkü onlar değerlidir
|
||||
|
||||
@@ -75,6 +75,8 @@ PODS:
|
||||
- Flutter
|
||||
- flutter_sodium (0.0.1):
|
||||
- Flutter
|
||||
- flutter_timezone (0.0.1):
|
||||
- Flutter
|
||||
- fluttertoast (0.0.2):
|
||||
- Flutter
|
||||
- GoogleDataTransport (10.1.0):
|
||||
@@ -259,6 +261,7 @@ DEPENDENCIES:
|
||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
- flutter_sodium (from `.symlinks/plugins/flutter_sodium/ios`)
|
||||
- flutter_timezone (from `.symlinks/plugins/flutter_timezone/ios`)
|
||||
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||
- home_widget (from `.symlinks/plugins/home_widget/ios`)
|
||||
- image_editor_common (from `.symlinks/plugins/image_editor_common/ios`)
|
||||
@@ -298,7 +301,7 @@ DEPENDENCIES:
|
||||
- wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`)
|
||||
|
||||
SPEC REPOS:
|
||||
https://github.com/ente-io/ffmpeg-kit-custom-repo-ios:
|
||||
https://github.com/ente-io/ffmpeg-kit-custom-repo-ios.git:
|
||||
- ffmpeg_kit_custom
|
||||
trunk:
|
||||
- Firebase
|
||||
@@ -359,6 +362,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||
flutter_sodium:
|
||||
:path: ".symlinks/plugins/flutter_sodium/ios"
|
||||
flutter_timezone:
|
||||
:path: ".symlinks/plugins/flutter_timezone/ios"
|
||||
fluttertoast:
|
||||
:path: ".symlinks/plugins/fluttertoast/ios"
|
||||
home_widget:
|
||||
@@ -435,81 +440,82 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/wakelock_plus/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
app_links: 76b66b60cc809390ca1ad69bfd66b998d2387ac7
|
||||
background_fetch: 94b36ee293e82972852dba8ede1fbcd3bd3d9d57
|
||||
battery_info: 83f3aae7be2fccefab1d2bf06b8aa96f11c8bcdd
|
||||
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
||||
cupertino_http: 94ac07f5ff090b8effa6c5e2c47871d48ab7c86c
|
||||
dart_ui_isolate: 46f6714abe6891313267153ef6f9748d8ecfcab1
|
||||
device_info_plus: 335f3ce08d2e174b9fdc3db3db0f4e3b1f66bd89
|
||||
app_links: f3e17e4ee5e357b39d8b95290a9b2c299fca71c6
|
||||
background_fetch: 39f11371c0dce04b001c4bfd5e782bcccb0a85e2
|
||||
battery_info: b6c551049266af31556b93c9d9b9452cfec0219f
|
||||
connectivity_plus: 2a701ffec2c0ae28a48cf7540e279787e77c447d
|
||||
cupertino_http: 947a233f40cfea55167a49f2facc18434ea117ba
|
||||
dart_ui_isolate: d5bcda83ca4b04f129d70eb90110b7a567aece14
|
||||
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
|
||||
ffmpeg_kit_custom: 682b4f2f1ff1f8abae5a92f6c3540f2441d5be99
|
||||
ffmpeg_kit_flutter: 915b345acc97d4142e8a9a8549d177ff10f043f5
|
||||
file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6
|
||||
ffmpeg_kit_flutter: 9dce4803991478c78c6fb9f972703301101095fe
|
||||
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
|
||||
Firebase: d80354ed7f6df5f9aca55e9eb47cc4b634735eaf
|
||||
firebase_core: 6cbed78b4f298ed103a9fd034e6dbc846320480f
|
||||
firebase_messaging: 5e0adf2eb18b0ee59aa0c109314c091a0497ecac
|
||||
firebase_core: 6e223dfa350b2edceb729cea505eaaef59330682
|
||||
firebase_messaging: 07fde77ae28c08616a1d4d870450efc2b38cf40d
|
||||
FirebaseCore: 99fe0c4b44a39f37d99e6404e02009d2db5d718d
|
||||
FirebaseCoreInternal: df24ce5af28864660ecbd13596fc8dd3a8c34629
|
||||
FirebaseInstallations: 6c963bd2a86aca0481eef4f48f5a4df783ae5917
|
||||
FirebaseMessaging: 487b634ccdf6f7b7ff180fdcb2a9935490f764e8
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
flutter_email_sender: aa1e9772696691d02cd91fea829856c11efb8e58
|
||||
flutter_image_compress_common: 1697a328fd72bfb335507c6bca1a65fa5ad87df1
|
||||
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
||||
flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100
|
||||
flutter_native_splash: 6cad9122ea0fad137d23137dd14b937f3e90b145
|
||||
flutter_secure_storage: 2c2ff13db9e0a5647389bff88b0ecac56e3f3418
|
||||
flutter_sodium: 7e4621538491834eba53bd524547854bcbbd6987
|
||||
fluttertoast: 2c67e14dce98bbdb200df9e1acf610d7a6264ea1
|
||||
flutter_email_sender: e03bdda7637bcd3539bfe718fddd980e9508efaa
|
||||
flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e
|
||||
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
|
||||
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
|
||||
flutter_native_splash: f71420956eb811e6d310720fee915f1d42852e7a
|
||||
flutter_secure_storage: 23fc622d89d073675f2eaa109381aefbcf5a49be
|
||||
flutter_sodium: a00383520fc689c688b66fd3092984174712493e
|
||||
flutter_timezone: ac3da59ac941ff1c98a2e1f0293420e020120282
|
||||
fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f
|
||||
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
|
||||
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
|
||||
home_widget: f169fc41fd807b4d46ab6615dc44d62adbf9f64f
|
||||
image_editor_common: 3de87e7c4804f4ae24c8f8a998362b98c105cac1
|
||||
in_app_purchase_storekit: d1a48cb0f8b29dbf5f85f782f5dd79b21b90a5e6
|
||||
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
|
||||
launcher_icon_switcher: 84c218d233505aa7d8655d8fa61a3ba802c022da
|
||||
home_widget: 0434835a4c9a75704264feff6be17ea40e0f0d57
|
||||
image_editor_common: d6f6644ae4a6de80481e89fe6d0a8c49e30b4b43
|
||||
in_app_purchase_storekit: a1ce04056e23eecc666b086040239da7619cd783
|
||||
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
|
||||
launcher_icon_switcher: 8e0ad2131a20c51c1dd939896ee32e70cd845b37
|
||||
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
|
||||
local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
|
||||
local_auth_ios: f7a1841beef3151d140a967c2e46f30637cdf451
|
||||
local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3
|
||||
local_auth_ios: 5046a18c018dd973247a0564496c8898dbb5adf9
|
||||
Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d
|
||||
maps_launcher: edf829809ba9e894d70e569bab11c16352dedb45
|
||||
media_extension: 671e2567880d96c95c65c9a82ccceed8f2e309fd
|
||||
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
|
||||
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
|
||||
motion_sensors: 741e702c17467b9569a92165dda8d4d88c6167f1
|
||||
motionphoto: 23e2aeb5c6380112f69468d71f970fa7438e5ed1
|
||||
move_to_background: 7e3467dd2a1d1013e98c9c1cb93fd53cd7ef9d84
|
||||
maps_launcher: 2e5b6a2d664ec6c27f82ffa81b74228d770ab203
|
||||
media_extension: 6618f07abd762cdbfaadf1b0c56a287e820f0c84
|
||||
media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1
|
||||
media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e
|
||||
motion_sensors: 03f55b7c637a7e365a0b5f9697a449f9059d5d91
|
||||
motionphoto: 8b65ce50c7d7ff3c767534fc3768b2eed9ac24e4
|
||||
move_to_background: cd3091014529ec7829e342ad2d75c0a11f4378a5
|
||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||
native_video_player: e363dd14f6a498ad8a8f7e6486a0db046ad19f13
|
||||
objective_c: 89e720c30d716b036faf9c9684022048eee1eee2
|
||||
onnxruntime: f9b296392c96c42882be020a59dbeac6310d81b2
|
||||
native_video_player: 5d36066807b680e181473e6890dde643ac85380d
|
||||
objective_c: 77e887b5ba1827970907e10e832eec1683f3431d
|
||||
onnxruntime: e7c2ae44385191eaad5ae64c935a72debaddc997
|
||||
onnxruntime-c: a909204639a1f035f575127ac406f781ac797c9c
|
||||
onnxruntime-objc: b6fab0f1787aa6f7190c2013f03037df4718bd8b
|
||||
open_mail_app: 7314a609e88eed22d53671279e189af7a0ab0f11
|
||||
open_mail_app: 70273c53f768beefdafbe310c3d9086e4da3cb02
|
||||
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
|
||||
photo_manager: d2fbcc0f2d82458700ee6256a15018210a81d413
|
||||
privacy_screen: 3159a541f5d3a31bea916cfd4e58f9dc722b3fd4
|
||||
package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
|
||||
photo_manager: ff695c7a1dd5bc379974953a2b5c0a293f7c4c8a
|
||||
privacy_screen: 1a131c052ceb3c3659934b003b0d397c2381a24e
|
||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||
receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00
|
||||
receive_sharing_intent: 79c848f5b045674ad60b9fea3bafea59962ad2c1
|
||||
SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868
|
||||
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380
|
||||
Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854
|
||||
sentry_flutter: 942017adbe00f963061cb11ec260414a990b7a42
|
||||
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
|
||||
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
|
||||
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
|
||||
sentry_flutter: 6a134f9d381e49f22ea25a67736cf0cf4d02ec9c
|
||||
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
|
||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||
sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d
|
||||
sqlite3: fc1400008a9b3525f5914ed715a5d1af0b8f4983
|
||||
sqlite3_flutter_libs: 3c323550ef3b928bc0aa9513c841e45a7d242832
|
||||
system_info_plus: 555ce7047fbbf29154726db942ae785c29211740
|
||||
ua_client_hints: 92fe0d139619b73ec9fcb46cc7e079a26178f586
|
||||
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
|
||||
video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b
|
||||
video_thumbnail: 584ccfa55d8fd2f3d5507218b0a18d84c839c620
|
||||
volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12
|
||||
wakelock_plus: 04623e3f525556020ebd4034310f20fe7fda8b49
|
||||
sqlite3_flutter_libs: 069c435986dd4b63461aecd68f4b30be4a9e9daa
|
||||
system_info_plus: 5393c8da281d899950d751713575fbf91c7709aa
|
||||
ua_client_hints: aeabd123262c087f0ce151ef96fa3ab77bfc8b38
|
||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||
video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
|
||||
video_thumbnail: 94ba6705afbaa120b77287080424930f23ea0c40
|
||||
volume_controller: 2e3de73d6e7e81a0067310d17fb70f2f86d71ac7
|
||||
wakelock_plus: 373cfe59b235a6dd5837d0fb88791d2f13a90d56
|
||||
|
||||
PODFILE CHECKSUM: a8ef88ad74ba499756207e7592c6071a96756d18
|
||||
|
||||
|
||||
@@ -389,6 +389,7 @@
|
||||
"${BUILT_PRODUCTS_DIR}/flutter_native_splash/flutter_native_splash.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/flutter_secure_storage/flutter_secure_storage.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/flutter_sodium/flutter_sodium.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/flutter_timezone/flutter_timezone.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/fluttertoast/fluttertoast.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/home_widget/home_widget.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/image_editor_common/image_editor_common.framework",
|
||||
@@ -483,6 +484,7 @@
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_native_splash.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_secure_storage.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_sodium.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/flutter_timezone.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/fluttertoast.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/home_widget.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_editor_common.framework",
|
||||
|
||||
@@ -101,12 +101,9 @@ const blackThumbnailBase64 = '/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAEBAQEBAQEB'
|
||||
'AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAo'
|
||||
'AKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD/9k=';
|
||||
|
||||
const localFileServer =
|
||||
String.fromEnvironment("localFileServer", defaultValue: "");
|
||||
|
||||
const uploadTempFilePrefix = "upload_file_";
|
||||
final tempDirCleanUpInterval = kDebugMode
|
||||
? const Duration(seconds: 30).inMicroseconds
|
||||
? const Duration(hours: 1).inMicroseconds
|
||||
: const Duration(hours: 6).inMicroseconds;
|
||||
|
||||
const kFilterChipHeight = 32.0;
|
||||
|
||||
@@ -640,7 +640,6 @@ class FilesDB with SqlDbBase {
|
||||
int visibility = visibleVisibility,
|
||||
DBFilterOptions? filterOptions,
|
||||
bool applyOwnerCheck = false,
|
||||
bool ignoreSharedFiles = false,
|
||||
}) async {
|
||||
final stopWatch = EnteWatch('getAllPendingOrUploadedFiles')..start();
|
||||
final order = (asc ?? false ? 'ASC' : 'DESC');
|
||||
@@ -663,7 +662,7 @@ class FilesDB with SqlDbBase {
|
||||
subQueries.add(' AND $columnMMdVisibility = ?');
|
||||
args.add(visibility);
|
||||
|
||||
if (ignoreSharedFiles == true) {
|
||||
if (filterOptions?.ignoreSharedItems ?? false) {
|
||||
subQueries.add(' AND $columnOwnerID = ?');
|
||||
args.add(ownerID);
|
||||
}
|
||||
@@ -696,7 +695,6 @@ class FilesDB with SqlDbBase {
|
||||
int ownerID, {
|
||||
int? limit,
|
||||
bool? asc,
|
||||
bool ignoreSharedFiles = false,
|
||||
required DBFilterOptions filterOptions,
|
||||
}) async {
|
||||
final db = await instance.sqliteAsyncDB;
|
||||
@@ -708,7 +706,7 @@ class FilesDB with SqlDbBase {
|
||||
'SELECT * FROM $filesTable WHERE $columnCreationTime >= ? AND $columnCreationTime <= ? AND ($columnMMdVisibility IS NULL OR $columnMMdVisibility = ?)'
|
||||
' AND ($columnLocalID IS NOT NULL OR ($columnCollectionID IS NOT NULL AND $columnCollectionID IS NOT -1))');
|
||||
|
||||
if (ignoreSharedFiles == true) {
|
||||
if (filterOptions.ignoreSharedItems) {
|
||||
subQueries.add(' AND $columnOwnerID = ?');
|
||||
args.add(ownerID);
|
||||
}
|
||||
@@ -1685,8 +1683,8 @@ class FilesDB with SqlDbBase {
|
||||
AND $columnUploadedFileID != -1
|
||||
AND $columnOwnerID = $userID
|
||||
AND $columnLocalID IS NOT NULL
|
||||
AND ($columnFileSize IS NULL OR $columnFileSize <= 524288000)
|
||||
AND ($columnDuration IS NULL OR $columnDuration <= 60)
|
||||
AND ($columnFileSize IS NOT NULL AND $columnFileSize <= 524288000)
|
||||
AND ($columnDuration IS NOT NULL AND ($columnDuration <= 60 AND $columnDuration > 0))
|
||||
ORDER BY $columnCreationTime DESC
|
||||
''',
|
||||
[getInt(fileType), beginDate.microsecondsSinceEpoch],
|
||||
|
||||
3
mobile/lib/events/album_sort_order_change_event.dart
Normal file
@@ -0,0 +1,3 @@
|
||||
import "package:photos/events/event.dart";
|
||||
|
||||
class AlbumSortOrderChangeEvent extends Event {}
|
||||
3
mobile/lib/events/clear_album_selections_event.dart
Normal file
@@ -0,0 +1,3 @@
|
||||
import "package:photos/events/event.dart";
|
||||
|
||||
class ClearAlbumSelectionsEvent extends Event {}
|
||||
@@ -1,10 +0,0 @@
|
||||
import "dart:collection";
|
||||
|
||||
import "package:photos/events/event.dart";
|
||||
import "package:photos/models/preview/preview_item.dart";
|
||||
|
||||
class PreviewUpdatedEvent extends Event {
|
||||
final LinkedHashMap<int, PreviewItem> items;
|
||||
|
||||
PreviewUpdatedEvent(this.items);
|
||||
}
|
||||
@@ -12,4 +12,12 @@ extension UserExtension on User {
|
||||
String? get linkedPersonID =>
|
||||
PersonService.instance.emailToPartialPersonDataMapCache[email]
|
||||
?[PersonService.kPersonIDKey];
|
||||
|
||||
String get nameOrEmail {
|
||||
if (PersonService.isInitialized) {
|
||||
return displayName ?? email.substring(0, email.indexOf("@"));
|
||||
} else {
|
||||
return email.substring(0, email.indexOf("@"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
400
mobile/lib/generated/intl/messages_ar.dart
generated
@@ -97,220 +97,224 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
static String m26(count, formattedSize) =>
|
||||
"${count} ملفات، ${formattedSize} لكل منها";
|
||||
|
||||
static String m27(newEmail) => "تم تغيير البريد الإلكتروني إلى ${newEmail}";
|
||||
static String m27(name) => "هذا البريد الإلكتروني مرتبط مسبقاً بـ ${name}.";
|
||||
|
||||
static String m28(email) => "${email} لا يملك حساب Ente.";
|
||||
static String m28(newEmail) => "تم تغيير البريد الإلكتروني إلى ${newEmail}";
|
||||
|
||||
static String m29(email) =>
|
||||
static String m29(email) => "${email} لا يملك حساب Ente.";
|
||||
|
||||
static String m30(email) =>
|
||||
"${email} لا يملك حسابًا على Ente.\n\nأرسل له دعوة لمشاركة الصور.";
|
||||
|
||||
static String m30(name) => "معانقة ${name}";
|
||||
static String m31(name) => "معانقة ${name}";
|
||||
|
||||
static String m31(text) => "تم العثور على صور إضافية لـ ${text}";
|
||||
static String m32(text) => "تم العثور على صور إضافية لـ ${text}";
|
||||
|
||||
static String m32(name) => "الاستمتاع بالطعام مع ${name}";
|
||||
|
||||
static String m33(count, formattedNumber) =>
|
||||
"${Intl.plural(count, one: 'ملف واحد', two: 'ملفان', few: '${formattedNumber} ملفات', many: '${formattedNumber} ملفًا', other: '${formattedNumber} ملفًا')} على هذا الجهاز تم نسخه احتياطيًا بأمان";
|
||||
static String m33(name) => "الاستمتاع بالطعام مع ${name}";
|
||||
|
||||
static String m34(count, formattedNumber) =>
|
||||
"${Intl.plural(count, one: 'ملف واحد', two: 'ملفان', few: '${formattedNumber} ملفات', many: '${formattedNumber} ملفًا', other: '${formattedNumber} ملفًا')} على هذا الجهاز تم نسخه احتياطيًا بأمان";
|
||||
|
||||
static String m35(count, formattedNumber) =>
|
||||
"${Intl.plural(count, one: 'ملف واحد', two: 'ملفان', few: '${formattedNumber} ملفات', many: '${formattedNumber} ملفًا', other: '${formattedNumber} ملفًا')} في هذا الألبوم تم نسخه احتياطيًا بأمان";
|
||||
|
||||
static String m35(storageAmountInGB) =>
|
||||
static String m36(storageAmountInGB) =>
|
||||
"${storageAmountInGB} جيجابايت مجانية في كل مرة يشترك فيها شخص بخطة مدفوعة ويطبق رمزك";
|
||||
|
||||
static String m36(endDate) => "التجربة المجانية صالحة حتى ${endDate}";
|
||||
static String m37(endDate) => "التجربة المجانية صالحة حتى ${endDate}";
|
||||
|
||||
static String m37(count) =>
|
||||
static String m38(count) =>
|
||||
"لا يزال بإمكانك الوصول ${Intl.plural(count, one: 'إليه', two: 'إليهما', other: 'إليها')} على Ente طالما لديك اشتراك نشط.";
|
||||
|
||||
static String m38(sizeInMBorGB) => "تحرير ${sizeInMBorGB}";
|
||||
static String m39(sizeInMBorGB) => "تحرير ${sizeInMBorGB}";
|
||||
|
||||
static String m39(count, formattedSize) =>
|
||||
static String m40(count, formattedSize) =>
|
||||
"${Intl.plural(count, one: 'يمكن حذفه من الجهاز لتحرير ${formattedSize}', two: 'يمكن حذفهما من الجهاز لتحرير ${formattedSize}', few: 'يمكن حذفها من الجهاز لتحرير ${formattedSize}', many: 'يمكن حذفها من الجهاز لتحرير ${formattedSize}', other: 'يمكن حذفها من الجهاز لتحرير ${formattedSize}')}";
|
||||
|
||||
static String m40(currentlyProcessing, totalCount) =>
|
||||
static String m41(currentlyProcessing, totalCount) =>
|
||||
"جارٍ المعالجة ${currentlyProcessing} / ${totalCount}";
|
||||
|
||||
static String m41(name) => "التنزه مع ${name}";
|
||||
static String m42(name) => "التنزه مع ${name}";
|
||||
|
||||
static String m42(count) =>
|
||||
static String m43(count) =>
|
||||
"${Intl.plural(count, one: '${count} عُنْصُر', other: '${count} عَنَاصِر')}";
|
||||
|
||||
static String m43(name) => "آخر مرة مع ${name}";
|
||||
static String m44(name) => "آخر مرة مع ${name}";
|
||||
|
||||
static String m44(email) => "${email} دعاك لتكون جهة اتصال موثوقة";
|
||||
static String m45(email) => "${email} دعاك لتكون جهة اتصال موثوقة";
|
||||
|
||||
static String m45(expiryTime) => "ستنتهي صلاحية الرابط في ${expiryTime}";
|
||||
static String m46(expiryTime) => "ستنتهي صلاحية الرابط في ${expiryTime}";
|
||||
|
||||
static String m46(email) => "ربط الشخص بـ ${email}";
|
||||
static String m47(email) => "ربط الشخص بـ ${email}";
|
||||
|
||||
static String m47(personName, email) =>
|
||||
static String m48(personName, email) =>
|
||||
"سيؤدي هذا إلى ربط ${personName} بـ ${email}";
|
||||
|
||||
static String m48(count, formattedCount) =>
|
||||
static String m49(count, formattedCount) =>
|
||||
"${Intl.plural(count, zero: 'لا توجد ذكريات', one: 'ذكرى واحدة', two: 'ذكريتان', few: '${formattedCount} ذكريات', many: '${formattedCount} ذكرى', other: '${formattedCount} ذكرى')}";
|
||||
|
||||
static String m49(count) =>
|
||||
static String m50(count) =>
|
||||
"${Intl.plural(count, one: 'نقل عنصر', two: 'نقل عنصرين', few: 'نقل ${count} عناصر', many: 'نقل ${count} عنصرًا', other: 'نقل ${count} عنصرًا')}";
|
||||
|
||||
static String m50(albumName) => "تم النقل بنجاح إلى ${albumName}";
|
||||
static String m51(albumName) => "تم النقل بنجاح إلى ${albumName}";
|
||||
|
||||
static String m51(personName) => "لا توجد اقتراحات لـ ${personName}";
|
||||
static String m52(personName) => "لا توجد اقتراحات لـ ${personName}";
|
||||
|
||||
static String m52(name) => "ليس ${name}؟";
|
||||
static String m53(name) => "ليس ${name}؟";
|
||||
|
||||
static String m53(familyAdminEmail) =>
|
||||
static String m54(familyAdminEmail) =>
|
||||
"يرجى الاتصال بـ ${familyAdminEmail} لتغيير الرمز الخاص بك.";
|
||||
|
||||
static String m54(name) => "الاحتفال مع ${name}";
|
||||
static String m55(name) => "الاحتفال مع ${name}";
|
||||
|
||||
static String m55(passwordStrengthValue) =>
|
||||
static String m56(passwordStrengthValue) =>
|
||||
"قوة كلمة المرور: ${passwordStrengthValue}";
|
||||
|
||||
static String m56(providerName) =>
|
||||
static String m57(providerName) =>
|
||||
"يرجى التواصل مع دعم ${providerName} إذا تم خصم المبلغ منك.";
|
||||
|
||||
static String m57(name, age) => "${name} يبلغ ${age}!";
|
||||
static String m58(name, age) => "${name} يبلغ ${age}!";
|
||||
|
||||
static String m58(name, age) => "${name} سيبلغ ${age} قريبًا";
|
||||
|
||||
static String m59(count) =>
|
||||
"${Intl.plural(count, zero: 'لا توجد صور', one: 'صورة واحدة', two: 'صورتان', few: '${count} صور', many: '${count} صورة', other: '${count} صورة')}";
|
||||
static String m59(name, age) => "${name} سيبلغ ${age} قريبًا";
|
||||
|
||||
static String m60(count) =>
|
||||
"${Intl.plural(count, zero: 'لا توجد صور', one: 'صورة واحدة', two: 'صورتان', few: '${count} صور', many: '${count} صورة', other: '${count} صورة')}";
|
||||
|
||||
static String m61(endDate) =>
|
||||
static String m61(count) =>
|
||||
"${Intl.plural(count, zero: 'لا توجد صور', one: 'صورة واحدة', two: 'صورتان', few: '${count} صور', many: '${count} صورة', other: '${count} صورة')}";
|
||||
|
||||
static String m62(endDate) =>
|
||||
"التجربة المجانية صالحة حتى ${endDate}.\nيمكنك اختيار خطة مدفوعة بعد ذلك.";
|
||||
|
||||
static String m62(toEmail) =>
|
||||
static String m63(toEmail) =>
|
||||
"يرجى مراسلتنا عبر البريد الإلكتروني على ${toEmail}";
|
||||
|
||||
static String m63(toEmail) => "يرجى إرسال السجلات إلى \n${toEmail}";
|
||||
static String m64(toEmail) => "يرجى إرسال السجلات إلى \n${toEmail}";
|
||||
|
||||
static String m64(name) => "التقاط صور مع ${name}";
|
||||
static String m65(name) => "التقاط صور مع ${name}";
|
||||
|
||||
static String m65(folderName) => "جارٍ معالجة ${folderName}...";
|
||||
static String m66(folderName) => "جارٍ معالجة ${folderName}...";
|
||||
|
||||
static String m66(storeName) => "قيّمنا على ${storeName}";
|
||||
static String m67(storeName) => "قيّمنا على ${storeName}";
|
||||
|
||||
static String m67(name) => "تمت إعادة تعيينك إلى ${name}";
|
||||
static String m68(name) => "تمت إعادة تعيينك إلى ${name}";
|
||||
|
||||
static String m68(days, email) =>
|
||||
static String m69(days, email) =>
|
||||
"يمكنك الوصول إلى الحساب بعد ${days} أيام. سيتم إرسال إشعار إلى ${email}.";
|
||||
|
||||
static String m69(email) =>
|
||||
static String m70(email) =>
|
||||
"يمكنك الآن استرداد حساب ${email} عن طريق تعيين كلمة مرور جديدة.";
|
||||
|
||||
static String m70(email) => "${email} يحاول استرداد حسابك.";
|
||||
static String m71(email) => "${email} يحاول استرداد حسابك.";
|
||||
|
||||
static String m71(storageInGB) =>
|
||||
static String m72(storageInGB) =>
|
||||
"3. تحصلون كلاكما على ${storageInGB} جيجابايت* مجانًا";
|
||||
|
||||
static String m72(userEmail) =>
|
||||
static String m73(userEmail) =>
|
||||
"سيتم إزالة ${userEmail} من هذا الألبوم المشترك.\n\nسيتم أيضًا إزالة أي صور أضافها إلى الألبوم.";
|
||||
|
||||
static String m73(endDate) => "يتجدد الاشتراك في ${endDate}";
|
||||
static String m74(endDate) => "يتجدد الاشتراك في ${endDate}";
|
||||
|
||||
static String m74(name) => "رحلة برية مع ${name}";
|
||||
static String m75(name) => "رحلة برية مع ${name}";
|
||||
|
||||
static String m75(count) =>
|
||||
static String m76(count) =>
|
||||
"${Intl.plural(count, one: '${count} النتائج التي تم العثور عليها', other: '${count} النتائج التي تم العثور عليها')}";
|
||||
|
||||
static String m76(snapshotLength, searchLength) =>
|
||||
static String m77(snapshotLength, searchLength) =>
|
||||
"عدم تطابق طول الأقسام: ${snapshotLength} != ${searchLength}";
|
||||
|
||||
static String m77(count) => "تم تحديد ${count}";
|
||||
static String m78(count) => "تم تحديد ${count}";
|
||||
|
||||
static String m78(count, yourCount) =>
|
||||
static String m79(count, yourCount) =>
|
||||
"تم تحديد ${count} (${yourCount} منها لك)";
|
||||
|
||||
static String m79(name) => "صور سيلفي مع ${name}";
|
||||
|
||||
static String m80(verificationID) =>
|
||||
"إليك معرّف التحقق الخاص بي لـ ente.io: ${verificationID}";
|
||||
static String m80(name) => "صور سيلفي مع ${name}";
|
||||
|
||||
static String m81(verificationID) =>
|
||||
"إليك معرّف التحقق الخاص بي لـ ente.io: ${verificationID}";
|
||||
|
||||
static String m82(verificationID) =>
|
||||
"مرحبًا، هل يمكنك تأكيد أن هذا هو معرّف التحقق الخاص بك على ente.io: ${verificationID}؟";
|
||||
|
||||
static String m82(referralCode, referralStorageInGB) =>
|
||||
static String m83(referralCode, referralStorageInGB) =>
|
||||
"رمز إحالة Ente الخاص بي: ${referralCode}\n\nطبقه في الإعدادات ← عام ← الإحالات للحصول على ${referralStorageInGB} جيجابايت مجانًا بعد الاشتراك في خطة مدفوعة.\n\nhttps://ente.io";
|
||||
|
||||
static String m83(numberOfPeople) =>
|
||||
static String m84(numberOfPeople) =>
|
||||
"${Intl.plural(numberOfPeople, zero: 'مشاركة مع أشخاص محددين', one: 'تمت المشاركة مع شخص واحد', two: 'تمت المشاركة مع شخصين', few: 'تمت المشاركة مع ${numberOfPeople} أشخاص', many: 'تمت المشاركة مع ${numberOfPeople} شخصًا', other: 'تمت المشاركة مع ${numberOfPeople} شخصًا')}";
|
||||
|
||||
static String m84(emailIDs) => "تمت المشاركة مع ${emailIDs}";
|
||||
static String m85(emailIDs) => "تمت المشاركة مع ${emailIDs}";
|
||||
|
||||
static String m85(fileType) => "سيتم حذف ${fileType} من جهازك.";
|
||||
static String m86(fileType) => "سيتم حذف ${fileType} من جهازك.";
|
||||
|
||||
static String m86(fileType) => "${fileType} موجود في Ente وعلى جهازك.";
|
||||
static String m87(fileType) => "${fileType} موجود في Ente وعلى جهازك.";
|
||||
|
||||
static String m87(fileType) => "سيتم حذف ${fileType} من Ente.";
|
||||
static String m88(fileType) => "سيتم حذف ${fileType} من Ente.";
|
||||
|
||||
static String m88(name) => "الرياضة مع ${name}";
|
||||
static String m89(name) => "الرياضة مع ${name}";
|
||||
|
||||
static String m89(name) => "تسليط الضوء على ${name}";
|
||||
static String m90(name) => "تسليط الضوء على ${name}";
|
||||
|
||||
static String m90(storageAmountInGB) => "${storageAmountInGB} جيجابايت";
|
||||
static String m91(storageAmountInGB) => "${storageAmountInGB} جيجابايت";
|
||||
|
||||
static String m91(
|
||||
static String m92(
|
||||
usedAmount, usedStorageUnit, totalAmount, totalStorageUnit) =>
|
||||
"تم استخدام ${usedAmount} ${usedStorageUnit} من ${totalAmount} ${totalStorageUnit}";
|
||||
|
||||
static String m92(id) =>
|
||||
static String m93(id) =>
|
||||
"تم ربط ${id} الخاص بك بحساب Ente آخر.\nإذا كنت ترغب في استخدام ${id} مع هذا الحساب، يرجى الاتصال بدعمنا.";
|
||||
|
||||
static String m93(endDate) => "سيتم إلغاء اشتراكك في ${endDate}";
|
||||
static String m94(endDate) => "سيتم إلغاء اشتراكك في ${endDate}";
|
||||
|
||||
static String m94(completed, total) => "${completed}/${total} ذكريات محفوظة";
|
||||
static String m95(completed, total) => "${completed}/${total} ذكريات محفوظة";
|
||||
|
||||
static String m95(ignoreReason) =>
|
||||
static String m96(ignoreReason) =>
|
||||
"انقر للتحميل، تم تجاهل التحميل حاليًا بسبب ${ignoreReason}";
|
||||
|
||||
static String m96(storageAmountInGB) =>
|
||||
static String m97(storageAmountInGB) =>
|
||||
"سيحصلون أيضًا على ${storageAmountInGB} جيجابايت";
|
||||
|
||||
static String m97(email) => "هذا هو معرّف التحقق الخاص بـ ${email}";
|
||||
static String m98(email) => "هذا هو معرّف التحقق الخاص بـ ${email}";
|
||||
|
||||
static String m98(count) =>
|
||||
static String m99(count) =>
|
||||
"${Intl.plural(count, one: 'هذا الأسبوع، قبل سنة', two: 'هذا الأسبوع، قبل سنتين', few: 'هذا الأسبوع، قبل ${count} سنوات', many: 'هذا الأسبوع، قبل ${count} سنة', other: 'هذا الأسبوع، قبل ${count} سنة')}";
|
||||
|
||||
static String m99(dateFormat) => "${dateFormat} عبر السنين";
|
||||
static String m100(dateFormat) => "${dateFormat} عبر السنين";
|
||||
|
||||
static String m100(count) =>
|
||||
static String m101(count) =>
|
||||
"${Intl.plural(count, zero: 'قريبًا', one: 'يوم واحد', two: 'يومان', few: '${count} أيام', many: '${count} يومًا', other: '${count} يومًا')}";
|
||||
|
||||
static String m101(year) => "رحلة في ${year}";
|
||||
static String m102(year) => "رحلة في ${year}";
|
||||
|
||||
static String m102(location) => "رحلة إلى ${location}";
|
||||
static String m103(location) => "رحلة إلى ${location}";
|
||||
|
||||
static String m103(email) =>
|
||||
static String m104(email) =>
|
||||
"لقد تمت دعوتك لتكون جهة اتصال موثوقة بواسطة ${email}.";
|
||||
|
||||
static String m104(galleryType) =>
|
||||
static String m105(galleryType) =>
|
||||
"نوع المعرض ${galleryType} غير مدعوم لإعادة التسمية.";
|
||||
|
||||
static String m105(ignoreReason) => "تم تجاهل التحميل بسبب ${ignoreReason}";
|
||||
static String m106(ignoreReason) => "تم تجاهل التحميل بسبب ${ignoreReason}";
|
||||
|
||||
static String m106(count) => "جارٍ حفظ ${count} ذكريات...";
|
||||
static String m107(count) => "جارٍ حفظ ${count} ذكريات...";
|
||||
|
||||
static String m107(endDate) => "صالح حتى ${endDate}";
|
||||
static String m108(endDate) => "صالح حتى ${endDate}";
|
||||
|
||||
static String m108(email) => "التحقق من ${email}";
|
||||
static String m109(email) => "التحقق من ${email}";
|
||||
|
||||
static String m109(count) =>
|
||||
"${Intl.plural(count, zero: 'تمت إضافة 0 مشاهدين', one: 'تمت إضافة مشاهد واحد', two: 'تمت إضافة مشاهدين', few: 'تمت إضافة ${count} مشاهدين', many: 'تمت إضافة ${count} مشاهدًا', other: 'تمت إضافة ${count} مشاهدًا')}";
|
||||
|
||||
static String m110(email) =>
|
||||
"لقد أرسلنا بريدًا إلكترونيًا إلى <green>${email}</green>";
|
||||
static String m110(name) => "عرض ${name} لإلغاء الربط";
|
||||
|
||||
static String m111(count) =>
|
||||
"${Intl.plural(count, zero: 'تمت إضافة 0 مشاهدين', one: 'تمت إضافة مشاهد واحد', two: 'تمت إضافة مشاهدين', few: 'تمت إضافة ${count} مشاهدين', many: 'تمت إضافة ${count} مشاهدًا', other: 'تمت إضافة ${count} مشاهدًا')}";
|
||||
|
||||
static String m112(email) =>
|
||||
"لقد أرسلنا بريدًا إلكترونيًا إلى <green>${email}</green>";
|
||||
|
||||
static String m113(count) =>
|
||||
"${Intl.plural(count, one: 'قبل سنة', two: 'قبل سنتين', few: 'قبل ${count} سنوات', many: 'قبل ${count} سنة', other: 'قبل ${count} سنة')}";
|
||||
|
||||
static String m112(name) => "أنت و ${name}";
|
||||
static String m114(name) => "أنت و ${name}";
|
||||
|
||||
static String m113(storageSaved) => "لقد حررت ${storageSaved} بنجاح!";
|
||||
static String m115(storageSaved) => "لقد حررت ${storageSaved} بنجاح!";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
@@ -324,7 +328,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("الحساب تم تكوينه بالفعل."),
|
||||
"accountOwnerPersonAppbarTitle": m0,
|
||||
"accountWelcomeBack":
|
||||
MessageLookupByLibrary.simpleMessage("مرحبًا مجددًا!"),
|
||||
MessageLookupByLibrary.simpleMessage("أهلاً بعودتك!"),
|
||||
"ackPasswordLostWarning": MessageLookupByLibrary.simpleMessage(
|
||||
"أدرك أنني إذا فقدت كلمة المرور، فقد أفقد بياناتي لأنها <underline>مشفرة بالكامل من طرف إلى طرف</underline>."),
|
||||
"activeSessions":
|
||||
@@ -532,24 +536,6 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"blackFridaySale":
|
||||
MessageLookupByLibrary.simpleMessage("تخفيضات الجمعة السوداء"),
|
||||
"blog": MessageLookupByLibrary.simpleMessage("المدونة"),
|
||||
"cLBulkEdit":
|
||||
MessageLookupByLibrary.simpleMessage("تعديل التواريخ بشكل جماعي"),
|
||||
"cLBulkEditDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"يمكنك الآن تحديد صور متعددة، وتعديل التاريخ/الوقت لجميعها بإجراء سريع واحد. تغيير التواريخ مدعوم أيضًا."),
|
||||
"cLFamilyPlan":
|
||||
MessageLookupByLibrary.simpleMessage("حدود الخطة العائلية"),
|
||||
"cLFamilyPlanDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"يمكنك الآن تعيين حدود لمقدار التخزين الذي يمكن لأفراد عائلتك استخدامه."),
|
||||
"cLIcon": MessageLookupByLibrary.simpleMessage("أيقونة جديدة"),
|
||||
"cLIconDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"أخيرًا، أيقونة تطبيق جديدة، نعتقد أنها تمثل عملنا على أفضل وجه. أضفنا أيضًا مبدل أيقونات حتى تتمكن من الاستمرار في استخدام الأيقونة القديمة."),
|
||||
"cLMemories": MessageLookupByLibrary.simpleMessage("الذكريات"),
|
||||
"cLMemoriesDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"أعد اكتشاف لحظاتك الخاصة - تسليط الضوء على الأشخاص المفضلين لديك، رحلاتك وعطلاتك، أفضل لقطاتك، وأكثر من ذلك بكثير. قم بتشغيل تعلم الآلة، ضع علامة على نفسك وقم بتسمية أصدقائك للحصول على أفضل تجربة."),
|
||||
"cLWidgets":
|
||||
MessageLookupByLibrary.simpleMessage("الأدوات المصغرة (Widgets)"),
|
||||
"cLWidgetsDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"الأدوات المصغرة للشاشة الرئيسية المدمجة مع الذكريات متاحة الآن. ستعرض لحظاتك الخاصة دون فتح التطبيق."),
|
||||
"cachedData": MessageLookupByLibrary.simpleMessage("البيانات المؤقتة"),
|
||||
"calculating": MessageLookupByLibrary.simpleMessage("جارٍ الحساب..."),
|
||||
"canNotOpenBody": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -848,6 +834,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"duplicateFileCountWithStorageSaved": m25,
|
||||
"duplicateItemsGroup": m26,
|
||||
"edit": MessageLookupByLibrary.simpleMessage("تعديل"),
|
||||
"editEmailAlreadyLinked": m27,
|
||||
"editLocation": MessageLookupByLibrary.simpleMessage("تعديل الموقع"),
|
||||
"editLocationTagTitle":
|
||||
MessageLookupByLibrary.simpleMessage("تعديل الموقع"),
|
||||
@@ -860,17 +847,17 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"eligible": MessageLookupByLibrary.simpleMessage("مؤهل"),
|
||||
"email": MessageLookupByLibrary.simpleMessage("البريد الإلكتروني"),
|
||||
"emailAlreadyRegistered": MessageLookupByLibrary.simpleMessage(
|
||||
"البريد الإلكتروني مُسَجَّل من قبل."),
|
||||
"emailChangedTo": m27,
|
||||
"emailDoesNotHaveEnteAccount": m28,
|
||||
"emailNoEnteAccount": m29,
|
||||
"البريد الإلكتروني مُسجل من قبل."),
|
||||
"emailChangedTo": m28,
|
||||
"emailDoesNotHaveEnteAccount": m29,
|
||||
"emailNoEnteAccount": m30,
|
||||
"emailNotRegistered":
|
||||
MessageLookupByLibrary.simpleMessage("البريد الإلكتروني غير مسجل."),
|
||||
"emailVerificationToggle": MessageLookupByLibrary.simpleMessage(
|
||||
"تأكيد عنوان البريد الإلكتروني"),
|
||||
"emailYourLogs": MessageLookupByLibrary.simpleMessage(
|
||||
"إرسال سجلاتك عبر البريد الإلكتروني"),
|
||||
"embracingThem": m30,
|
||||
"embracingThem": m31,
|
||||
"emergencyContacts":
|
||||
MessageLookupByLibrary.simpleMessage("جهات اتصال الطوارئ"),
|
||||
"empty": MessageLookupByLibrary.simpleMessage("إفراغ"),
|
||||
@@ -946,7 +933,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"exportYourData": MessageLookupByLibrary.simpleMessage("تصدير بياناتك"),
|
||||
"extraPhotosFound":
|
||||
MessageLookupByLibrary.simpleMessage("تم العثور على صور إضافية"),
|
||||
"extraPhotosFoundFor": m31,
|
||||
"extraPhotosFoundFor": m32,
|
||||
"faceNotClusteredYet": MessageLookupByLibrary.simpleMessage(
|
||||
"لم يتم تجميع الوجه بعد، يرجى العودة لاحقًا"),
|
||||
"faceRecognition":
|
||||
@@ -981,7 +968,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"faq": MessageLookupByLibrary.simpleMessage("الأسئلة الشائعة"),
|
||||
"faqs": MessageLookupByLibrary.simpleMessage("الأسئلة الشائعة"),
|
||||
"favorite": MessageLookupByLibrary.simpleMessage("المفضلة"),
|
||||
"feastingWithThem": m32,
|
||||
"feastingWithThem": m33,
|
||||
"feedback": MessageLookupByLibrary.simpleMessage("ملاحظات"),
|
||||
"file": MessageLookupByLibrary.simpleMessage("ملف"),
|
||||
"fileFailedToSaveToGallery":
|
||||
@@ -995,8 +982,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"fileTypes": MessageLookupByLibrary.simpleMessage("أنواع الملفات"),
|
||||
"fileTypesAndNames":
|
||||
MessageLookupByLibrary.simpleMessage("أنواع وأسماء الملفات"),
|
||||
"filesBackedUpFromDevice": m33,
|
||||
"filesBackedUpInAlbum": m34,
|
||||
"filesBackedUpFromDevice": m34,
|
||||
"filesBackedUpInAlbum": m35,
|
||||
"filesDeleted": MessageLookupByLibrary.simpleMessage("تم حذف الملفات."),
|
||||
"filesSavedToGallery":
|
||||
MessageLookupByLibrary.simpleMessage("تم حفظ الملفات في المعرض."),
|
||||
@@ -1013,26 +1000,26 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("الوجوه التي تم العثور عليها"),
|
||||
"freeStorageClaimed": MessageLookupByLibrary.simpleMessage(
|
||||
"تم المطالبة بمساحة التخزين المجانية"),
|
||||
"freeStorageOnReferralSuccess": m35,
|
||||
"freeStorageOnReferralSuccess": m36,
|
||||
"freeStorageUsable": MessageLookupByLibrary.simpleMessage(
|
||||
"مساحة تخزين مجانية متاحة للاستخدام"),
|
||||
"freeTrial": MessageLookupByLibrary.simpleMessage("تجربة مجانية"),
|
||||
"freeTrialValidTill": m36,
|
||||
"freeUpAccessPostDelete": m37,
|
||||
"freeUpAmount": m38,
|
||||
"freeTrialValidTill": m37,
|
||||
"freeUpAccessPostDelete": m38,
|
||||
"freeUpAmount": m39,
|
||||
"freeUpDeviceSpace":
|
||||
MessageLookupByLibrary.simpleMessage("تحرير مساحة على الجهاز"),
|
||||
"freeUpDeviceSpaceDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"وفر مساحة على جهازك عن طريق مسح الملفات التي تم نسخها احتياطيًا."),
|
||||
"freeUpSpace": MessageLookupByLibrary.simpleMessage("تحرير المساحة"),
|
||||
"freeUpSpaceSaving": m39,
|
||||
"freeUpSpaceSaving": m40,
|
||||
"gallery": MessageLookupByLibrary.simpleMessage("المعرض"),
|
||||
"galleryMemoryLimitInfo": MessageLookupByLibrary.simpleMessage(
|
||||
"يتم عرض ما يصل إلى 1000 ذكرى في المعرض."),
|
||||
"general": MessageLookupByLibrary.simpleMessage("عام"),
|
||||
"generatingEncryptionKeys": MessageLookupByLibrary.simpleMessage(
|
||||
"جارٍ إنشاء مفاتيح التشفير..."),
|
||||
"genericProgress": m40,
|
||||
"genericProgress": m41,
|
||||
"goToSettings":
|
||||
MessageLookupByLibrary.simpleMessage("الانتقال إلى الإعدادات"),
|
||||
"googlePlayId":
|
||||
@@ -1061,7 +1048,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"hideSharedItemsFromHomeGallery": MessageLookupByLibrary.simpleMessage(
|
||||
"إخفاء العناصر المشتركة من معرض الصفحة الرئيسية"),
|
||||
"hiding": MessageLookupByLibrary.simpleMessage("جارٍ الإخفاء..."),
|
||||
"hikingWithThem": m41,
|
||||
"hikingWithThem": m42,
|
||||
"hostedAtOsmFrance":
|
||||
MessageLookupByLibrary.simpleMessage("مستضاف في OSM France"),
|
||||
"howItWorks": MessageLookupByLibrary.simpleMessage("كيف يعمل"),
|
||||
@@ -1116,7 +1103,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"itLooksLikeSomethingWentWrongPleaseRetryAfterSome":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"يبدو أن خطأً ما قد حدث. يرجى المحاولة مرة أخرى بعد بعض الوقت. إذا استمر الخطأ، يرجى الاتصال بفريق الدعم لدينا."),
|
||||
"itemCount": m42,
|
||||
"itemCount": m43,
|
||||
"itemsShowTheNumberOfDaysRemainingBeforePermanentDeletion":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"تعرض العناصر عدد الأيام المتبقية قبل الحذف الدائم."),
|
||||
@@ -1138,7 +1125,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"kindlyHelpUsWithThisInformation": MessageLookupByLibrary.simpleMessage(
|
||||
"يرجى مساعدتنا بهذه المعلومات"),
|
||||
"language": MessageLookupByLibrary.simpleMessage("اللغة"),
|
||||
"lastTimeWithThem": m43,
|
||||
"lastTimeWithThem": m44,
|
||||
"lastUpdated": MessageLookupByLibrary.simpleMessage("آخر تحديث"),
|
||||
"lastYearsTrip":
|
||||
MessageLookupByLibrary.simpleMessage("رحلة العام الماضي"),
|
||||
@@ -1152,7 +1139,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"legacy": MessageLookupByLibrary.simpleMessage("جهات الاتصال الموثوقة"),
|
||||
"legacyAccounts":
|
||||
MessageLookupByLibrary.simpleMessage("الحسابات الموثوقة"),
|
||||
"legacyInvite": m44,
|
||||
"legacyInvite": m45,
|
||||
"legacyPageDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"تسمح جهات الاتصال الموثوقة لأشخاص معينين بالوصول إلى حسابك في حالة غيابك."),
|
||||
"legacyPageDesc2": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -1169,7 +1156,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("لمشاركة أسرع"),
|
||||
"linkEnabled": MessageLookupByLibrary.simpleMessage("مفعّل"),
|
||||
"linkExpired": MessageLookupByLibrary.simpleMessage("منتهي الصلاحية"),
|
||||
"linkExpiresOn": m45,
|
||||
"linkExpiresOn": m46,
|
||||
"linkExpiry":
|
||||
MessageLookupByLibrary.simpleMessage("انتهاء صلاحية الرابط"),
|
||||
"linkHasExpired":
|
||||
@@ -1178,13 +1165,13 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"linkPerson": MessageLookupByLibrary.simpleMessage("ربط الشخص"),
|
||||
"linkPersonCaption":
|
||||
MessageLookupByLibrary.simpleMessage("لتجربة مشاركة أفضل"),
|
||||
"linkPersonToEmail": m46,
|
||||
"linkPersonToEmailConfirmation": m47,
|
||||
"linkPersonToEmail": m47,
|
||||
"linkPersonToEmailConfirmation": m48,
|
||||
"livePhotos": MessageLookupByLibrary.simpleMessage("الصور الحية"),
|
||||
"loadMessage1": MessageLookupByLibrary.simpleMessage(
|
||||
"يمكنك مشاركة اشتراكك مع عائلتك."),
|
||||
"loadMessage2": MessageLookupByLibrary.simpleMessage(
|
||||
"لقد حفظنا أكثر من 30 مليون ذكرى حتى الآن."),
|
||||
"لقد حفظنا أكثر من 200 مليون ذكرى حتى الآن."),
|
||||
"loadMessage3": MessageLookupByLibrary.simpleMessage(
|
||||
"نحتفظ بـ 3 نسخ من بياناتك، إحداها في ملجأ للطوارئ تحت الأرض."),
|
||||
"loadMessage4": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -1268,7 +1255,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"),
|
||||
"matrix": MessageLookupByLibrary.simpleMessage("Matrix"),
|
||||
"me": MessageLookupByLibrary.simpleMessage("أنا"),
|
||||
"memoryCount": m48,
|
||||
"memoryCount": m49,
|
||||
"merchandise":
|
||||
MessageLookupByLibrary.simpleMessage("المنتجات الترويجية"),
|
||||
"mergeWithExisting":
|
||||
@@ -1300,13 +1287,13 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"mostRecent": MessageLookupByLibrary.simpleMessage("الأحدث"),
|
||||
"mostRelevant": MessageLookupByLibrary.simpleMessage("الأكثر صلة"),
|
||||
"mountains": MessageLookupByLibrary.simpleMessage("فوق التلال"),
|
||||
"moveItem": m49,
|
||||
"moveItem": m50,
|
||||
"moveSelectedPhotosToOneDate": MessageLookupByLibrary.simpleMessage(
|
||||
"نقل الصور المحددة إلى تاريخ واحد"),
|
||||
"moveToAlbum": MessageLookupByLibrary.simpleMessage("نقل إلى ألبوم"),
|
||||
"moveToHiddenAlbum":
|
||||
MessageLookupByLibrary.simpleMessage("نقل إلى الألبوم المخفي"),
|
||||
"movedSuccessfullyTo": m50,
|
||||
"movedSuccessfullyTo": m51,
|
||||
"movedToTrash":
|
||||
MessageLookupByLibrary.simpleMessage("تم النقل إلى سلة المهملات"),
|
||||
"movingFilesToAlbum": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -1361,10 +1348,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"noResults": MessageLookupByLibrary.simpleMessage("لا توجد نتائج"),
|
||||
"noResultsFound":
|
||||
MessageLookupByLibrary.simpleMessage("لم يتم العثور على نتائج."),
|
||||
"noSuggestionsForPerson": m51,
|
||||
"noSuggestionsForPerson": m52,
|
||||
"noSystemLockFound":
|
||||
MessageLookupByLibrary.simpleMessage("لم يتم العثور على قفل نظام."),
|
||||
"notPersonLabel": m52,
|
||||
"notPersonLabel": m53,
|
||||
"notThisPerson": MessageLookupByLibrary.simpleMessage("ليس هذا الشخص؟"),
|
||||
"nothingSharedWithYouYet": MessageLookupByLibrary.simpleMessage(
|
||||
"لم تتم مشاركة أي شيء معك بعد"),
|
||||
@@ -1377,7 +1364,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"على <branding>Ente</branding>"),
|
||||
"onTheRoad":
|
||||
MessageLookupByLibrary.simpleMessage("على الطريق مرة أخرى"),
|
||||
"onlyFamilyAdminCanChangeCode": m53,
|
||||
"onThisDay": MessageLookupByLibrary.simpleMessage("On this day"),
|
||||
"onlyFamilyAdminCanChangeCode": m54,
|
||||
"onlyThem": MessageLookupByLibrary.simpleMessage("هم فقط"),
|
||||
"oops": MessageLookupByLibrary.simpleMessage("عفوًا"),
|
||||
"oopsCouldNotSaveEdits":
|
||||
@@ -1407,7 +1395,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"pairingComplete":
|
||||
MessageLookupByLibrary.simpleMessage("اكتمل الإقران"),
|
||||
"panorama": MessageLookupByLibrary.simpleMessage("بانوراما"),
|
||||
"partyWithThem": m54,
|
||||
"partyWithThem": m55,
|
||||
"passKeyPendingVerification":
|
||||
MessageLookupByLibrary.simpleMessage("التحقق لا يزال معلقًا."),
|
||||
"passkey": MessageLookupByLibrary.simpleMessage("مفتاح المرور"),
|
||||
@@ -1415,19 +1403,19 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("التحقق من مفتاح المرور"),
|
||||
"password": MessageLookupByLibrary.simpleMessage("كلمة المرور"),
|
||||
"passwordChangedSuccessfully":
|
||||
MessageLookupByLibrary.simpleMessage("تم تغيير كلمة المرور بنجاح."),
|
||||
MessageLookupByLibrary.simpleMessage("تم تغيير كلمة المرور بنجاح"),
|
||||
"passwordLock": MessageLookupByLibrary.simpleMessage("قفل بكلمة مرور"),
|
||||
"passwordStrength": m55,
|
||||
"passwordStrength": m56,
|
||||
"passwordStrengthInfo": MessageLookupByLibrary.simpleMessage(
|
||||
"يتم حساب قوة كلمة المرور مع الأخذ في الاعتبار طول كلمة المرور، والأحرف المستخدمة، وما إذا كانت كلمة المرور تظهر في قائمة أفضل 10,000 كلمة مرور شائعة الاستخدام."),
|
||||
"passwordWarning": MessageLookupByLibrary.simpleMessage(
|
||||
"نحن لا نخزن كلمة المرور هذه، لذا إذا نسيتها، <underline>لا يمكننا المساعدة في فك تشفير بياناتك</underline>."),
|
||||
"نحن لا نقوم بتخزين كلمة المرور هذه، لذا إذا نسيتها، <underline>لا يمكننا فك تشفير بياناتك</underline>"),
|
||||
"paymentDetails": MessageLookupByLibrary.simpleMessage("تفاصيل الدفع"),
|
||||
"paymentFailed":
|
||||
MessageLookupByLibrary.simpleMessage("فشلت عملية الدفع"),
|
||||
"paymentFailedMessage": MessageLookupByLibrary.simpleMessage(
|
||||
"للأسف، فشلت عملية الدفع الخاصة بك. يرجى الاتصال بالدعم وسوف نساعدك!"),
|
||||
"paymentFailedTalkToProvider": m56,
|
||||
"paymentFailedTalkToProvider": m57,
|
||||
"pendingItems": MessageLookupByLibrary.simpleMessage("العناصر المعلقة"),
|
||||
"pendingSync": MessageLookupByLibrary.simpleMessage("المزامنة المعلقة"),
|
||||
"people": MessageLookupByLibrary.simpleMessage("الأشخاص"),
|
||||
@@ -1438,20 +1426,20 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"permanentlyDelete": MessageLookupByLibrary.simpleMessage("حذف نهائي"),
|
||||
"permanentlyDeleteFromDevice":
|
||||
MessageLookupByLibrary.simpleMessage("حذف نهائي من الجهاز؟"),
|
||||
"personIsAge": m57,
|
||||
"personIsAge": m58,
|
||||
"personName": MessageLookupByLibrary.simpleMessage("اسم الشخص"),
|
||||
"personTurningAge": m58,
|
||||
"personTurningAge": m59,
|
||||
"pets": MessageLookupByLibrary.simpleMessage("رفاق فروي"),
|
||||
"photoDescriptions":
|
||||
MessageLookupByLibrary.simpleMessage("أوصاف الصور"),
|
||||
"photoGridSize": MessageLookupByLibrary.simpleMessage("حجم شبكة الصور"),
|
||||
"photoSmallCase": MessageLookupByLibrary.simpleMessage("صورة"),
|
||||
"photocountPhotos": m59,
|
||||
"photocountPhotos": m60,
|
||||
"photos": MessageLookupByLibrary.simpleMessage("الصور"),
|
||||
"photosAddedByYouWillBeRemovedFromTheAlbum":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"ستتم إزالة الصور التي أضفتها من الألبوم."),
|
||||
"photosCount": m60,
|
||||
"photosCount": m61,
|
||||
"photosKeepRelativeTimeDifference":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"تحتفظ الصور بالفرق الزمني النسبي"),
|
||||
@@ -1462,7 +1450,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"playOnTv":
|
||||
MessageLookupByLibrary.simpleMessage("تشغيل الألبوم على التلفزيون"),
|
||||
"playOriginal": MessageLookupByLibrary.simpleMessage("تشغيل الأصلي"),
|
||||
"playStoreFreeTrialValidTill": m61,
|
||||
"playStoreFreeTrialValidTill": m62,
|
||||
"playStream": MessageLookupByLibrary.simpleMessage("تشغيل البث"),
|
||||
"playstoreSubscription":
|
||||
MessageLookupByLibrary.simpleMessage("اشتراك متجر Play"),
|
||||
@@ -1475,14 +1463,14 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"pleaseContactSupportIfTheProblemPersists":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"يرجى الاتصال بالدعم إذا استمرت المشكلة."),
|
||||
"pleaseEmailUsAt": m62,
|
||||
"pleaseEmailUsAt": m63,
|
||||
"pleaseGrantPermissions":
|
||||
MessageLookupByLibrary.simpleMessage("يرجى منح الأذونات."),
|
||||
"pleaseLoginAgain":
|
||||
MessageLookupByLibrary.simpleMessage("يرجى تسجيل الدخول مرة أخرى."),
|
||||
"pleaseSelectQuickLinksToRemove": MessageLookupByLibrary.simpleMessage(
|
||||
"يرجى تحديد الروابط السريعة للإزالة."),
|
||||
"pleaseSendTheLogsTo": m63,
|
||||
"pleaseSendTheLogsTo": m64,
|
||||
"pleaseTryAgain":
|
||||
MessageLookupByLibrary.simpleMessage("يرجى المحاولة مرة أخرى"),
|
||||
"pleaseVerifyTheCodeYouHaveEntered":
|
||||
@@ -1496,7 +1484,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"يرجى الانتظار لبعض الوقت قبل إعادة المحاولة."),
|
||||
"pleaseWaitThisWillTakeAWhile": MessageLookupByLibrary.simpleMessage(
|
||||
"يرجى الانتظار، قد يستغرق هذا بعض الوقت."),
|
||||
"posingWithThem": m64,
|
||||
"posingWithThem": m65,
|
||||
"preparingLogs":
|
||||
MessageLookupByLibrary.simpleMessage("جارٍ تحضير السجلات..."),
|
||||
"preserveMore": MessageLookupByLibrary.simpleMessage("حفظ المزيد"),
|
||||
@@ -1514,7 +1502,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"proceed": MessageLookupByLibrary.simpleMessage("متابعة"),
|
||||
"processed": MessageLookupByLibrary.simpleMessage("تمت المعالجة"),
|
||||
"processing": MessageLookupByLibrary.simpleMessage("المعالجة"),
|
||||
"processingImport": m65,
|
||||
"processingImport": m66,
|
||||
"processingVideos":
|
||||
MessageLookupByLibrary.simpleMessage("معالجة مقاطع الفيديو"),
|
||||
"publicLinkCreated":
|
||||
@@ -1527,10 +1515,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"raiseTicket": MessageLookupByLibrary.simpleMessage("فتح تذكرة دعم"),
|
||||
"rateTheApp": MessageLookupByLibrary.simpleMessage("تقييم التطبيق"),
|
||||
"rateUs": MessageLookupByLibrary.simpleMessage("تقييم التطبيق"),
|
||||
"rateUsOnStore": m66,
|
||||
"rateUsOnStore": m67,
|
||||
"reassignMe":
|
||||
MessageLookupByLibrary.simpleMessage("إعادة تعيين \"أنا\""),
|
||||
"reassignedToName": m67,
|
||||
"reassignedToName": m68,
|
||||
"reassigningLoading":
|
||||
MessageLookupByLibrary.simpleMessage("جارٍ إعادة التعيين..."),
|
||||
"recover": MessageLookupByLibrary.simpleMessage("استعادة"),
|
||||
@@ -1541,7 +1529,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("استرداد الحساب"),
|
||||
"recoveryInitiated":
|
||||
MessageLookupByLibrary.simpleMessage("بدء الاسترداد"),
|
||||
"recoveryInitiatedDesc": m68,
|
||||
"recoveryInitiatedDesc": m69,
|
||||
"recoveryKey": MessageLookupByLibrary.simpleMessage("مفتاح الاسترداد"),
|
||||
"recoveryKeyCopiedToClipboard": MessageLookupByLibrary.simpleMessage(
|
||||
"تم نسخ مفتاح الاسترداد إلى الحافظة"),
|
||||
@@ -1555,12 +1543,12 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"تم التحقق من مفتاح الاسترداد."),
|
||||
"recoveryKeyVerifyReason": MessageLookupByLibrary.simpleMessage(
|
||||
"مفتاح الاسترداد هو الطريقة الوحيدة لاستعادة صورك إذا نسيت كلمة المرور. يمكنك العثور عليه في الإعدادات > الحساب.\n\nالرجاء إدخال مفتاح الاسترداد هنا للتحقق من أنك حفظته بشكل صحيح."),
|
||||
"recoveryReady": m69,
|
||||
"recoveryReady": m70,
|
||||
"recoverySuccessful":
|
||||
MessageLookupByLibrary.simpleMessage("تم الاسترداد بنجاح!"),
|
||||
"recoveryWarning": MessageLookupByLibrary.simpleMessage(
|
||||
"جهة اتصال موثوقة تحاول الوصول إلى حسابك"),
|
||||
"recoveryWarningBody": m70,
|
||||
"recoveryWarningBody": m71,
|
||||
"recreatePasswordBody": MessageLookupByLibrary.simpleMessage(
|
||||
"لا يمكن التحقق من كلمة المرور على جهازك الحالي، لكن يمكننا تعديلها لتعمل على جميع الأجهزة.\n\nسجّل الدخول باستخدام مفتاح الاسترداد، ثم أنشئ كلمة مرور جديدة (يمكنك اختيار نفس الكلمة السابقة إذا أردت)."),
|
||||
"recreatePasswordTitle":
|
||||
@@ -1576,7 +1564,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("1. أعطِ هذا الرمز لأصدقائك"),
|
||||
"referralStep2":
|
||||
MessageLookupByLibrary.simpleMessage("2. يشتركون في خطة مدفوعة"),
|
||||
"referralStep3": m71,
|
||||
"referralStep3": m72,
|
||||
"referrals": MessageLookupByLibrary.simpleMessage("الإحالات"),
|
||||
"referralsAreCurrentlyPaused":
|
||||
MessageLookupByLibrary.simpleMessage("الإحالات متوقفة مؤقتًا"),
|
||||
@@ -1605,7 +1593,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"removeLink": MessageLookupByLibrary.simpleMessage("إزالة الرابط"),
|
||||
"removeParticipant":
|
||||
MessageLookupByLibrary.simpleMessage("إزالة المشارك"),
|
||||
"removeParticipantBody": m72,
|
||||
"removeParticipantBody": m73,
|
||||
"removePersonLabel":
|
||||
MessageLookupByLibrary.simpleMessage("إزالة تسمية الشخص"),
|
||||
"removePublicLink":
|
||||
@@ -1626,7 +1614,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"renameFile": MessageLookupByLibrary.simpleMessage("إعادة تسمية الملف"),
|
||||
"renewSubscription":
|
||||
MessageLookupByLibrary.simpleMessage("تجديد الاشتراك"),
|
||||
"renewsOn": m73,
|
||||
"renewsOn": m74,
|
||||
"reportABug": MessageLookupByLibrary.simpleMessage("الإبلاغ عن خطأ"),
|
||||
"reportBug": MessageLookupByLibrary.simpleMessage("الإبلاغ عن خطأ"),
|
||||
"resendEmail": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -1652,7 +1640,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"reviewSuggestions":
|
||||
MessageLookupByLibrary.simpleMessage("مراجعة الاقتراحات"),
|
||||
"right": MessageLookupByLibrary.simpleMessage("يمين"),
|
||||
"roadtripWithThem": m74,
|
||||
"roadtripWithThem": m75,
|
||||
"rotate": MessageLookupByLibrary.simpleMessage("تدوير"),
|
||||
"rotateLeft": MessageLookupByLibrary.simpleMessage("تدوير لليسار"),
|
||||
"rotateRight": MessageLookupByLibrary.simpleMessage("تدوير لليمين"),
|
||||
@@ -1706,8 +1694,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"ادعُ الأشخاص، وسترى جميع الصور التي شاركوها هنا."),
|
||||
"searchPersonsEmptySection": MessageLookupByLibrary.simpleMessage(
|
||||
"سيتم عرض الأشخاص هنا بمجرد اكتمال المعالجة والمزامنة."),
|
||||
"searchResultCount": m75,
|
||||
"searchSectionsLengthMismatch": m76,
|
||||
"searchResultCount": m76,
|
||||
"searchSectionsLengthMismatch": m77,
|
||||
"security": MessageLookupByLibrary.simpleMessage("الأمان"),
|
||||
"seePublicAlbumLinksInApp": MessageLookupByLibrary.simpleMessage(
|
||||
"رؤية روابط الألبومات العامة في التطبيق"),
|
||||
@@ -1752,9 +1740,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"selectedItemsWillBeRemovedFromThisPerson":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"سيتم إزالة العناصر المحددة من هذا الشخص، ولكن لن يتم حذفها من مكتبتك."),
|
||||
"selectedPhotos": m77,
|
||||
"selectedPhotosWithYours": m78,
|
||||
"selfiesWithThem": m79,
|
||||
"selectedPhotos": m78,
|
||||
"selectedPhotosWithYours": m79,
|
||||
"selfiesWithThem": m80,
|
||||
"send": MessageLookupByLibrary.simpleMessage("إرسال"),
|
||||
"sendEmail":
|
||||
MessageLookupByLibrary.simpleMessage("إرسال بريد إلكتروني"),
|
||||
@@ -1784,16 +1772,16 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"shareAnAlbumNow":
|
||||
MessageLookupByLibrary.simpleMessage("شارك ألبومًا الآن"),
|
||||
"shareLink": MessageLookupByLibrary.simpleMessage("مشاركة الرابط"),
|
||||
"shareMyVerificationID": m80,
|
||||
"shareMyVerificationID": m81,
|
||||
"shareOnlyWithThePeopleYouWant": MessageLookupByLibrary.simpleMessage(
|
||||
"شارك فقط مع الأشخاص الذين تريدهم."),
|
||||
"shareTextConfirmOthersVerificationID": m81,
|
||||
"shareTextConfirmOthersVerificationID": m82,
|
||||
"shareTextRecommendUsingEnte": MessageLookupByLibrary.simpleMessage(
|
||||
"قم بتنزيل تطبيق Ente حتى نتمكن من مشاركة الصور ومقاطع الفيديو بالجودة الأصلية بسهولة.\n\nhttps://ente.io"),
|
||||
"shareTextReferralCode": m82,
|
||||
"shareTextReferralCode": m83,
|
||||
"shareWithNonenteUsers": MessageLookupByLibrary.simpleMessage(
|
||||
"المشاركة مع غير مستخدمي Ente"),
|
||||
"shareWithPeopleSectionTitle": m83,
|
||||
"shareWithPeopleSectionTitle": m84,
|
||||
"shareYourFirstAlbum":
|
||||
MessageLookupByLibrary.simpleMessage("شارك ألبومك الأول"),
|
||||
"sharedAlbumSectionDescription": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -1806,7 +1794,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"إشعارات الصور المشتركة الجديدة"),
|
||||
"sharedPhotoNotificationsExplanation": MessageLookupByLibrary.simpleMessage(
|
||||
"تلقّ إشعارات عندما يضيف شخص ما صورة إلى ألبوم مشترك أنت جزء منه."),
|
||||
"sharedWith": m84,
|
||||
"sharedWith": m85,
|
||||
"sharedWithMe":
|
||||
MessageLookupByLibrary.simpleMessage("تمت مشاركتها معي"),
|
||||
"sharedWithYou":
|
||||
@@ -1824,11 +1812,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"تسجيل الخروج من الأجهزة الأخرى"),
|
||||
"signUpTerms": MessageLookupByLibrary.simpleMessage(
|
||||
"أوافق على <u-terms>شروط الخدمة</u-terms> و<u-policy>سياسة الخصوصية</u-policy>"),
|
||||
"singleFileDeleteFromDevice": m85,
|
||||
"singleFileDeleteFromDevice": m86,
|
||||
"singleFileDeleteHighlight": MessageLookupByLibrary.simpleMessage(
|
||||
"سيتم حذفه من جميع الألبومات."),
|
||||
"singleFileInBothLocalAndRemote": m86,
|
||||
"singleFileInRemoteOnly": m87,
|
||||
"singleFileInBothLocalAndRemote": m87,
|
||||
"singleFileInRemoteOnly": m88,
|
||||
"skip": MessageLookupByLibrary.simpleMessage("تخط"),
|
||||
"social": MessageLookupByLibrary.simpleMessage("التواصل الاجتماعي"),
|
||||
"someItemsAreInBothEnteAndYourDevice":
|
||||
@@ -1846,6 +1834,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"حدث خطأ ما، يرجى المحاولة مرة أخرى"),
|
||||
"sorry": MessageLookupByLibrary.simpleMessage("عفوًا"),
|
||||
"sorryBackupFailedDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"عذرًا، لم نتمكن من عمل نسخة احتياطية لهذا الملف الآن، سنعيد المحاولة لاحقًا."),
|
||||
"sorryCouldNotAddToFavorites": MessageLookupByLibrary.simpleMessage(
|
||||
"عذرًا، تعذرت الإضافة إلى المفضلة!"),
|
||||
"sorryCouldNotRemoveFromFavorites":
|
||||
@@ -1862,8 +1852,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"sortNewestFirst": MessageLookupByLibrary.simpleMessage("الأحدث أولاً"),
|
||||
"sortOldestFirst": MessageLookupByLibrary.simpleMessage("الأقدم أولاً"),
|
||||
"sparkleSuccess": MessageLookupByLibrary.simpleMessage("✨ نجاح"),
|
||||
"sportsWithThem": m88,
|
||||
"spotlightOnThem": m89,
|
||||
"sportsWithThem": m89,
|
||||
"spotlightOnThem": m90,
|
||||
"spotlightOnYourself":
|
||||
MessageLookupByLibrary.simpleMessage("تسليط الضوء عليك"),
|
||||
"startAccountRecoveryTitle":
|
||||
@@ -1877,14 +1867,14 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"storage": MessageLookupByLibrary.simpleMessage("التخزين"),
|
||||
"storageBreakupFamily": MessageLookupByLibrary.simpleMessage("العائلة"),
|
||||
"storageBreakupYou": MessageLookupByLibrary.simpleMessage("أنت"),
|
||||
"storageInGB": m90,
|
||||
"storageInGB": m91,
|
||||
"storageLimitExceeded":
|
||||
MessageLookupByLibrary.simpleMessage("تم تجاوز حد التخزين."),
|
||||
"storageUsageInfo": m91,
|
||||
"storageUsageInfo": m92,
|
||||
"streamDetails": MessageLookupByLibrary.simpleMessage("تفاصيل البث"),
|
||||
"strongStrength": MessageLookupByLibrary.simpleMessage("قوية"),
|
||||
"subAlreadyLinkedErrMessage": m92,
|
||||
"subWillBeCancelledOn": m93,
|
||||
"subAlreadyLinkedErrMessage": m93,
|
||||
"subWillBeCancelledOn": m94,
|
||||
"subscribe": MessageLookupByLibrary.simpleMessage("اشتراك"),
|
||||
"subscribeToEnableSharing": MessageLookupByLibrary.simpleMessage(
|
||||
"المشاركة متاحة فقط للاشتراكات المدفوعة النشطة."),
|
||||
@@ -1901,7 +1891,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"suggestFeatures": MessageLookupByLibrary.simpleMessage("اقتراح ميزة"),
|
||||
"sunrise": MessageLookupByLibrary.simpleMessage("على الأفق"),
|
||||
"support": MessageLookupByLibrary.simpleMessage("الدعم"),
|
||||
"syncProgress": m94,
|
||||
"syncProgress": m95,
|
||||
"syncStopped": MessageLookupByLibrary.simpleMessage("توقفت المزامنة"),
|
||||
"syncing": MessageLookupByLibrary.simpleMessage("جارٍ المزامنة..."),
|
||||
"systemTheme": MessageLookupByLibrary.simpleMessage("النظام"),
|
||||
@@ -1910,7 +1900,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("انقر لإدخال الرمز"),
|
||||
"tapToUnlock": MessageLookupByLibrary.simpleMessage("انقر لفتح القفل"),
|
||||
"tapToUpload": MessageLookupByLibrary.simpleMessage("انقر للتحميل"),
|
||||
"tapToUploadIsIgnoredDue": m95,
|
||||
"tapToUploadIsIgnoredDue": m96,
|
||||
"tempErrorContactSupportIfPersists": MessageLookupByLibrary.simpleMessage(
|
||||
"يبدو أن خطأً ما قد حدث. يرجى المحاولة مرة أخرى بعد بعض الوقت. إذا استمر الخطأ، يرجى الاتصال بفريق الدعم لدينا."),
|
||||
"terminate": MessageLookupByLibrary.simpleMessage("إنهاء"),
|
||||
@@ -1934,7 +1924,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"theseItemsWillBeDeletedFromYourDevice":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"سيتم حذف هذه العناصر من جهازك."),
|
||||
"theyAlsoGetXGb": m96,
|
||||
"theyAlsoGetXGb": m97,
|
||||
"theyWillBeDeletedFromAllAlbums": MessageLookupByLibrary.simpleMessage(
|
||||
"سيتم حذفها من جميع الألبومات."),
|
||||
"thisActionCannotBeUndone": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -1951,12 +1941,12 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"thisImageHasNoExifData": MessageLookupByLibrary.simpleMessage(
|
||||
"لا تحتوي هذه الصورة على بيانات EXIF."),
|
||||
"thisIsMeExclamation": MessageLookupByLibrary.simpleMessage("هذا أنا!"),
|
||||
"thisIsPersonVerificationId": m97,
|
||||
"thisIsPersonVerificationId": m98,
|
||||
"thisIsYourVerificationId": MessageLookupByLibrary.simpleMessage(
|
||||
"هذا هو معرّف التحقق الخاص بك"),
|
||||
"thisWeekThroughTheYears":
|
||||
MessageLookupByLibrary.simpleMessage("هذا الأسبوع عبر السنين"),
|
||||
"thisWeekXYearsAgo": m98,
|
||||
"thisWeekXYearsAgo": m99,
|
||||
"thisWillLogYouOutOfTheFollowingDevice":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"سيؤدي هذا إلى تسجيل خروجك من الجهاز التالي:"),
|
||||
@@ -1968,7 +1958,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"thisWillRemovePublicLinksOfAllSelectedQuickLinks":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"سيؤدي هذا إلى إزالة الروابط العامة لجميع الروابط السريعة المحددة."),
|
||||
"throughTheYears": m99,
|
||||
"throughTheYears": m100,
|
||||
"toEnableAppLockPleaseSetupDevicePasscodeOrScreen":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"لتمكين قفل التطبيق، يرجى إعداد رمز مرور الجهاز أو قفل الشاشة في إعدادات النظام."),
|
||||
@@ -1982,13 +1972,13 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"total": MessageLookupByLibrary.simpleMessage("المجموع"),
|
||||
"totalSize": MessageLookupByLibrary.simpleMessage("الحجم الإجمالي"),
|
||||
"trash": MessageLookupByLibrary.simpleMessage("سلة المهملات"),
|
||||
"trashDaysLeft": m100,
|
||||
"trashDaysLeft": m101,
|
||||
"trim": MessageLookupByLibrary.simpleMessage("قص"),
|
||||
"tripInYear": m101,
|
||||
"tripToLocation": m102,
|
||||
"tripInYear": m102,
|
||||
"tripToLocation": m103,
|
||||
"trustedContacts":
|
||||
MessageLookupByLibrary.simpleMessage("جهات الاتصال الموثوقة"),
|
||||
"trustedInviteBody": m103,
|
||||
"trustedInviteBody": m104,
|
||||
"tryAgain": MessageLookupByLibrary.simpleMessage("المحاولة مرة أخرى"),
|
||||
"turnOnBackupForAutoUpload": MessageLookupByLibrary.simpleMessage(
|
||||
"قم بتشغيل النسخ الاحتياطي لتحميل الملفات المضافة إلى مجلد الجهاز هذا تلقائيًا إلى Ente."),
|
||||
@@ -2005,7 +1995,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"تمت إعادة تعيين المصادقة الثنائية بنجاح."),
|
||||
"twofactorSetup":
|
||||
MessageLookupByLibrary.simpleMessage("إعداد المصادقة الثنائية"),
|
||||
"typeOfGallerGallerytypeIsNotSupportedForRename": m104,
|
||||
"typeOfGallerGallerytypeIsNotSupportedForRename": m105,
|
||||
"unarchive": MessageLookupByLibrary.simpleMessage("إلغاء الأرشفة"),
|
||||
"unarchiveAlbum":
|
||||
MessageLookupByLibrary.simpleMessage("إلغاء أرشفة الألبوم"),
|
||||
@@ -2029,10 +2019,10 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"updatingFolderSelection":
|
||||
MessageLookupByLibrary.simpleMessage("جارٍ تحديث تحديد المجلد..."),
|
||||
"upgrade": MessageLookupByLibrary.simpleMessage("ترقية"),
|
||||
"uploadIsIgnoredDueToIgnorereason": m105,
|
||||
"uploadIsIgnoredDueToIgnorereason": m106,
|
||||
"uploadingFilesToAlbum": MessageLookupByLibrary.simpleMessage(
|
||||
"جارٍ تحميل الملفات إلى الألبوم..."),
|
||||
"uploadingMultipleMemories": m106,
|
||||
"uploadingMultipleMemories": m107,
|
||||
"uploadingSingleMemory":
|
||||
MessageLookupByLibrary.simpleMessage("جارٍ حفظ ذكرى واحدة..."),
|
||||
"upto50OffUntil4thDec": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -2050,7 +2040,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"useSelectedPhoto":
|
||||
MessageLookupByLibrary.simpleMessage("استخدام الصورة المحددة"),
|
||||
"usedSpace": MessageLookupByLibrary.simpleMessage("المساحة المستخدمة"),
|
||||
"validTill": m107,
|
||||
"validTill": m108,
|
||||
"verificationFailedPleaseTryAgain":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"فشل التحقق، يرجى المحاولة مرة أخرى."),
|
||||
@@ -2058,7 +2048,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"verify": MessageLookupByLibrary.simpleMessage("التحقق"),
|
||||
"verifyEmail":
|
||||
MessageLookupByLibrary.simpleMessage("التحقق من البريد الإلكتروني"),
|
||||
"verifyEmailID": m108,
|
||||
"verifyEmailID": m109,
|
||||
"verifyIDLabel": MessageLookupByLibrary.simpleMessage("تحقق"),
|
||||
"verifyPasskey":
|
||||
MessageLookupByLibrary.simpleMessage("التحقق من مفتاح المرور"),
|
||||
@@ -2069,7 +2059,8 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"جارٍ التحقق من مفتاح الاسترداد..."),
|
||||
"videoInfo": MessageLookupByLibrary.simpleMessage("معلومات الفيديو"),
|
||||
"videoSmallCase": MessageLookupByLibrary.simpleMessage("فيديو"),
|
||||
"videoStreaming": MessageLookupByLibrary.simpleMessage("بث الفيديو"),
|
||||
"videoStreaming":
|
||||
MessageLookupByLibrary.simpleMessage("مقاطع فيديو قابلة للبث"),
|
||||
"videos": MessageLookupByLibrary.simpleMessage("مقاطع الفيديو"),
|
||||
"viewActiveSessions":
|
||||
MessageLookupByLibrary.simpleMessage("عرض الجلسات النشطة"),
|
||||
@@ -2082,10 +2073,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"viewLargeFilesDesc": MessageLookupByLibrary.simpleMessage(
|
||||
"عرض الملفات التي تستهلك أكبر قدر من مساحة التخزين."),
|
||||
"viewLogs": MessageLookupByLibrary.simpleMessage("عرض السجلات"),
|
||||
"viewPersonToUnlink": m110,
|
||||
"viewRecoveryKey":
|
||||
MessageLookupByLibrary.simpleMessage("عرض مفتاح الاسترداد"),
|
||||
"viewer": MessageLookupByLibrary.simpleMessage("مشاهد"),
|
||||
"viewersSuccessfullyAdded": m109,
|
||||
"viewersSuccessfullyAdded": m111,
|
||||
"visitWebToManage": MessageLookupByLibrary.simpleMessage(
|
||||
"يرجى زيارة web.ente.io لإدارة اشتراكك."),
|
||||
"waitingForVerification":
|
||||
@@ -2098,7 +2090,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"weDontSupportEditingPhotosAndAlbumsThatYouDont":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"لا ندعم تعديل الصور والألبومات التي لا تملكها بعد."),
|
||||
"weHaveSendEmailTo": m110,
|
||||
"weHaveSendEmailTo": m112,
|
||||
"weakStrength": MessageLookupByLibrary.simpleMessage("ضعيفة"),
|
||||
"welcomeBack": MessageLookupByLibrary.simpleMessage("أهلاً بعودتك!"),
|
||||
"whatsNew": MessageLookupByLibrary.simpleMessage("ما الجديد"),
|
||||
@@ -2106,7 +2098,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"يمكن لجهة الاتصال الموثوقة المساعدة في استعادة بياناتك."),
|
||||
"yearShort": MessageLookupByLibrary.simpleMessage("سنة"),
|
||||
"yearly": MessageLookupByLibrary.simpleMessage("سنويًا"),
|
||||
"yearsAgo": m111,
|
||||
"yearsAgo": m113,
|
||||
"yes": MessageLookupByLibrary.simpleMessage("نعم"),
|
||||
"yesCancel": MessageLookupByLibrary.simpleMessage("نعم، إلغاء"),
|
||||
"yesConvertToViewer":
|
||||
@@ -2120,7 +2112,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"yesResetPerson":
|
||||
MessageLookupByLibrary.simpleMessage("نعم، إعادة تعيين الشخص"),
|
||||
"you": MessageLookupByLibrary.simpleMessage("أنت"),
|
||||
"youAndThem": m112,
|
||||
"youAndThem": m114,
|
||||
"youAreOnAFamilyPlan":
|
||||
MessageLookupByLibrary.simpleMessage("أنت مشترك في خطة عائلية!"),
|
||||
"youAreOnTheLatestVersion":
|
||||
@@ -2139,9 +2131,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("لا يمكنك المشاركة مع نفسك."),
|
||||
"youDontHaveAnyArchivedItems": MessageLookupByLibrary.simpleMessage(
|
||||
"لا توجد لديك أي عناصر مؤرشفة."),
|
||||
"youHaveSuccessfullyFreedUp": m113,
|
||||
"youHaveSuccessfullyFreedUp": m115,
|
||||
"yourAccountHasBeenDeleted":
|
||||
MessageLookupByLibrary.simpleMessage("تم حذف حسابك بنجاح."),
|
||||
MessageLookupByLibrary.simpleMessage("تم حذف حسابك بنجاح"),
|
||||
"yourMap": MessageLookupByLibrary.simpleMessage("خريطتك"),
|
||||
"yourPlanWasSuccessfullyDowngraded":
|
||||
MessageLookupByLibrary.simpleMessage("تم تخفيض خطتك بنجاح."),
|
||||
|
||||
13
mobile/lib/generated/intl/messages_be.dart
generated
@@ -20,12 +20,12 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||
class MessageLookup extends MessageLookupByLibrary {
|
||||
String get localeName => 'be';
|
||||
|
||||
static String m55(passwordStrengthValue) =>
|
||||
static String m56(passwordStrengthValue) =>
|
||||
"Надзейнасць пароля: ${passwordStrengthValue}";
|
||||
|
||||
static String m90(storageAmountInGB) => "${storageAmountInGB} Гб";
|
||||
static String m91(storageAmountInGB) => "${storageAmountInGB} Гб";
|
||||
|
||||
static String m110(email) =>
|
||||
static String m112(email) =>
|
||||
"Ліст адпраўлены на электронную пошту <green>${email}</green>";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
@@ -181,13 +181,14 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"Вашы даныя не могуць быць расшыфраваны без пароля або ключа аднаўлення па прычыне архітэктуры наша пратакола скразнога шыфравання"),
|
||||
"notifications": MessageLookupByLibrary.simpleMessage("Апавяшчэнні"),
|
||||
"ok": MessageLookupByLibrary.simpleMessage("Добра"),
|
||||
"onThisDay": MessageLookupByLibrary.simpleMessage("On this day"),
|
||||
"oops": MessageLookupByLibrary.simpleMessage("Вой"),
|
||||
"oopsSomethingWentWrong":
|
||||
MessageLookupByLibrary.simpleMessage("Штосьці пайшло не так"),
|
||||
"password": MessageLookupByLibrary.simpleMessage("Пароль"),
|
||||
"passwordChangedSuccessfully":
|
||||
MessageLookupByLibrary.simpleMessage("Пароль паспяхова зменены"),
|
||||
"passwordStrength": m55,
|
||||
"passwordStrength": m56,
|
||||
"passwordWarning": MessageLookupByLibrary.simpleMessage(
|
||||
"Мы не захоўваем гэты пароль і <underline>мы не зможам расшыфраваць вашы даныя</underline>, калі вы забудзеце яго"),
|
||||
"photoSmallCase": MessageLookupByLibrary.simpleMessage("фота"),
|
||||
@@ -249,7 +250,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Немагчыма згенерыраваць ключы бяспекі на гэтай прыладзе.\n\nЗарэгіструйцеся з іншай прылады."),
|
||||
"status": MessageLookupByLibrary.simpleMessage("Стан"),
|
||||
"storageInGB": m90,
|
||||
"storageInGB": m91,
|
||||
"strongStrength": MessageLookupByLibrary.simpleMessage("Надзейны"),
|
||||
"support": MessageLookupByLibrary.simpleMessage("Падтрымка"),
|
||||
"systemTheme": MessageLookupByLibrary.simpleMessage("Сістэма"),
|
||||
@@ -288,7 +289,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"videoSmallCase": MessageLookupByLibrary.simpleMessage("відэа"),
|
||||
"viewLargeFiles": MessageLookupByLibrary.simpleMessage("Вялікія файлы"),
|
||||
"viewer": MessageLookupByLibrary.simpleMessage("Праглядальнік"),
|
||||
"weHaveSendEmailTo": m110,
|
||||
"weHaveSendEmailTo": m112,
|
||||
"weakStrength": MessageLookupByLibrary.simpleMessage("Ненадзейны"),
|
||||
"welcomeBack": MessageLookupByLibrary.simpleMessage("З вяртаннем!"),
|
||||
"yesDelete": MessageLookupByLibrary.simpleMessage("Так, выдаліць"),
|
||||
|
||||
4
mobile/lib/generated/intl/messages_bg.dart
generated
@@ -21,5 +21,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
String get localeName => 'bg';
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{};
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
"onThisDay": MessageLookupByLibrary.simpleMessage("On this day")
|
||||
};
|
||||
}
|
||||
|
||||
4
mobile/lib/generated/intl/messages_ca.dart
generated
@@ -21,5 +21,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
String get localeName => 'ca';
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{};
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
"onThisDay": MessageLookupByLibrary.simpleMessage("On this day")
|
||||
};
|
||||
}
|
||||
|
||||
295
mobile/lib/generated/intl/messages_cs.dart
generated
@@ -20,19 +20,43 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||
class MessageLookup extends MessageLookupByLibrary {
|
||||
String get localeName => 'cs';
|
||||
|
||||
static String m6(albumName) => "Úspěšně přidáno do ${albumName}";
|
||||
|
||||
static String m46(expiryTime) => "Platnost odkazu vyprší ${expiryTime}";
|
||||
|
||||
static String m67(storeName) => "Ohodnoťte nás na ${storeName}";
|
||||
|
||||
static String m74(endDate) => "Předplatné se obnoví ${endDate}";
|
||||
|
||||
static String m80(name) => "Selfie s ${name}";
|
||||
|
||||
static String m109(email) => "Ověřit ${email}";
|
||||
|
||||
static String m114(name) => "Vy a ${name}";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
"aNewVersionOfEnteIsAvailable": MessageLookupByLibrary.simpleMessage(
|
||||
"Je dostupná nová verze Ente."),
|
||||
"acceptTrustInvite":
|
||||
MessageLookupByLibrary.simpleMessage("Přijmout pozvání"),
|
||||
"account": MessageLookupByLibrary.simpleMessage("Účet"),
|
||||
"accountWelcomeBack":
|
||||
MessageLookupByLibrary.simpleMessage("Vítejte zpět!"),
|
||||
"activeSessions":
|
||||
MessageLookupByLibrary.simpleMessage("Aktivní relace"),
|
||||
"add": MessageLookupByLibrary.simpleMessage("Přidat"),
|
||||
"addFiles": MessageLookupByLibrary.simpleMessage("Přidat soubory"),
|
||||
"addLocation": MessageLookupByLibrary.simpleMessage("Přidat polohu"),
|
||||
"addLocationButton": MessageLookupByLibrary.simpleMessage("Přidat"),
|
||||
"addMore": MessageLookupByLibrary.simpleMessage("Přidat další"),
|
||||
"addName": MessageLookupByLibrary.simpleMessage("Přidat název"),
|
||||
"addNew": MessageLookupByLibrary.simpleMessage("Přidat nový"),
|
||||
"addNewPerson":
|
||||
MessageLookupByLibrary.simpleMessage("Přidat novou osobu"),
|
||||
"addPhotos": MessageLookupByLibrary.simpleMessage("Přidat fotky"),
|
||||
"addToAlbum": MessageLookupByLibrary.simpleMessage("Přidat do alba"),
|
||||
"addedSuccessfullyTo": m6,
|
||||
"advancedSettings": MessageLookupByLibrary.simpleMessage("Pokročilé"),
|
||||
"after1Day": MessageLookupByLibrary.simpleMessage("Po 1 dni"),
|
||||
"after1Hour": MessageLookupByLibrary.simpleMessage("Po 1 hodině"),
|
||||
@@ -40,99 +64,232 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"after1Week": MessageLookupByLibrary.simpleMessage("Po 1 týdnu"),
|
||||
"after1Year": MessageLookupByLibrary.simpleMessage("Po 1 roce"),
|
||||
"albumOwner": MessageLookupByLibrary.simpleMessage("Vlastník"),
|
||||
"albumUpdated":
|
||||
MessageLookupByLibrary.simpleMessage("Album bylo aktualizováno"),
|
||||
"albums": MessageLookupByLibrary.simpleMessage("Alba"),
|
||||
"allow": MessageLookupByLibrary.simpleMessage("Povolit"),
|
||||
"androidBiometricRequiredTitle": MessageLookupByLibrary.simpleMessage(
|
||||
"Je požadováno biometrické ověření"),
|
||||
"androidBiometricSuccess":
|
||||
MessageLookupByLibrary.simpleMessage("Úspěšně dokončeno"),
|
||||
"androidCancelButton": MessageLookupByLibrary.simpleMessage("Zrušit"),
|
||||
"appleId": MessageLookupByLibrary.simpleMessage("Apple ID"),
|
||||
"apply": MessageLookupByLibrary.simpleMessage("Použít"),
|
||||
"archiveAlbum":
|
||||
MessageLookupByLibrary.simpleMessage("Archivovat album"),
|
||||
"archiving": MessageLookupByLibrary.simpleMessage("Archivování..."),
|
||||
"areYouSureYouWantToLogout":
|
||||
MessageLookupByLibrary.simpleMessage("Opravdu se chcete odhlásit?"),
|
||||
"askDeleteReason": MessageLookupByLibrary.simpleMessage(
|
||||
"Jaký je váš hlavní důvod, proč mažete svůj účet?"),
|
||||
"autoLock":
|
||||
MessageLookupByLibrary.simpleMessage("Automatické zamykání"),
|
||||
"backedUpFolders":
|
||||
MessageLookupByLibrary.simpleMessage("Zálohované složky"),
|
||||
"backup": MessageLookupByLibrary.simpleMessage("Zálohovat"),
|
||||
"backupFile": MessageLookupByLibrary.simpleMessage("Zálohovat soubor"),
|
||||
"backupStatus": MessageLookupByLibrary.simpleMessage("Stav zálohování"),
|
||||
"birthday": MessageLookupByLibrary.simpleMessage("Narozeniny"),
|
||||
"blog": MessageLookupByLibrary.simpleMessage("Blog"),
|
||||
"cLIcon": MessageLookupByLibrary.simpleMessage("Nová ikona"),
|
||||
"cLMemories": MessageLookupByLibrary.simpleMessage("Vzpomínky"),
|
||||
"cachedData":
|
||||
MessageLookupByLibrary.simpleMessage("Data uložená v mezipaměti"),
|
||||
"calculating":
|
||||
MessageLookupByLibrary.simpleMessage("Probíhá výpočet..."),
|
||||
"cancel": MessageLookupByLibrary.simpleMessage("Zrušit"),
|
||||
"cancelAccountRecovery":
|
||||
MessageLookupByLibrary.simpleMessage("Zrušit obnovení"),
|
||||
"cannotDeleteSharedFiles": MessageLookupByLibrary.simpleMessage(
|
||||
"Sdílené soubory nelze odstranit"),
|
||||
"changeEmail": MessageLookupByLibrary.simpleMessage("Změnit e-mail"),
|
||||
"changePassword": MessageLookupByLibrary.simpleMessage("Změnit heslo"),
|
||||
"checkForUpdates":
|
||||
MessageLookupByLibrary.simpleMessage("Zkontrolovat aktualizace"),
|
||||
"checkInboxAndSpamFolder": MessageLookupByLibrary.simpleMessage(
|
||||
"Zkontrolujte prosím svou doručenou poštu (a spam) pro dokončení ověření"),
|
||||
"checkStatus":
|
||||
MessageLookupByLibrary.simpleMessage("Zkontrolovat stav"),
|
||||
"checking": MessageLookupByLibrary.simpleMessage("Probíhá kontrola..."),
|
||||
"clearCaches":
|
||||
MessageLookupByLibrary.simpleMessage("Vymazat mezipaměť"),
|
||||
"clearIndexes": MessageLookupByLibrary.simpleMessage("Smazat indexy"),
|
||||
"close": MessageLookupByLibrary.simpleMessage("Zavřít"),
|
||||
"codeAppliedPageTitle":
|
||||
MessageLookupByLibrary.simpleMessage("Kód byl použit"),
|
||||
"collaborator": MessageLookupByLibrary.simpleMessage("Spolupracovník"),
|
||||
"collageLayout": MessageLookupByLibrary.simpleMessage("Rozvržení"),
|
||||
"color": MessageLookupByLibrary.simpleMessage("Barva"),
|
||||
"configuration": MessageLookupByLibrary.simpleMessage("Nastavení"),
|
||||
"confirm": MessageLookupByLibrary.simpleMessage("Potvrdit"),
|
||||
"confirmAccountDeletion":
|
||||
MessageLookupByLibrary.simpleMessage("Potvrdit odstranění účtu"),
|
||||
"contactSupport":
|
||||
MessageLookupByLibrary.simpleMessage("Kontaktovat podporu"),
|
||||
"contacts": MessageLookupByLibrary.simpleMessage("Kontakty"),
|
||||
"continueLabel": MessageLookupByLibrary.simpleMessage("Pokračovat"),
|
||||
"count": MessageLookupByLibrary.simpleMessage("Počet"),
|
||||
"crashReporting":
|
||||
MessageLookupByLibrary.simpleMessage("Hlášení o pádu"),
|
||||
"create": MessageLookupByLibrary.simpleMessage("Vytvořit"),
|
||||
"createAccount": MessageLookupByLibrary.simpleMessage("Vytvořit účet"),
|
||||
"createNewAccount":
|
||||
MessageLookupByLibrary.simpleMessage("Vytvořit nový účet"),
|
||||
"crop": MessageLookupByLibrary.simpleMessage("Oříznout"),
|
||||
"darkTheme": MessageLookupByLibrary.simpleMessage("Tmavý"),
|
||||
"dayToday": MessageLookupByLibrary.simpleMessage("Dnes"),
|
||||
"dayYesterday": MessageLookupByLibrary.simpleMessage("Včera"),
|
||||
"declineTrustInvite":
|
||||
MessageLookupByLibrary.simpleMessage("Odmítnout pozvání"),
|
||||
"decrypting": MessageLookupByLibrary.simpleMessage("Dešifrování..."),
|
||||
"decryptingVideo":
|
||||
MessageLookupByLibrary.simpleMessage("Dešifrování videa..."),
|
||||
"delete": MessageLookupByLibrary.simpleMessage("Smazat"),
|
||||
"deleteAccount": MessageLookupByLibrary.simpleMessage("Smazat účet"),
|
||||
"deleteAccountPermanentlyButton":
|
||||
MessageLookupByLibrary.simpleMessage("Trvale smazat účet"),
|
||||
"deleteAlbum": MessageLookupByLibrary.simpleMessage("Odstranit album"),
|
||||
"deleteAll": MessageLookupByLibrary.simpleMessage("Smazat vše"),
|
||||
"deleteEmptyAlbums":
|
||||
MessageLookupByLibrary.simpleMessage("Smazat prázdná alba"),
|
||||
"deleteEmptyAlbumsWithQuestionMark":
|
||||
MessageLookupByLibrary.simpleMessage("Smazat prázdná alba?"),
|
||||
"deleteFromBoth":
|
||||
MessageLookupByLibrary.simpleMessage("Odstranit z obou"),
|
||||
"deleteFromDevice":
|
||||
MessageLookupByLibrary.simpleMessage("Odstranit ze zařízení"),
|
||||
"deleteFromEnte":
|
||||
MessageLookupByLibrary.simpleMessage("Odstranit z Ente"),
|
||||
"deleteLocation":
|
||||
MessageLookupByLibrary.simpleMessage("Odstranit polohu"),
|
||||
"deleteReason1": MessageLookupByLibrary.simpleMessage(
|
||||
"Chybí klíčová funkce, kterou potřebuji"),
|
||||
"deleteReason2": MessageLookupByLibrary.simpleMessage(
|
||||
"Aplikace nebo určitá funkce se nechová tak, jak si myslím, že by měla"),
|
||||
"deleteReason3": MessageLookupByLibrary.simpleMessage(
|
||||
"Našel jsem jinou službu, která se mi líbí více"),
|
||||
"deleteReason4":
|
||||
MessageLookupByLibrary.simpleMessage("Můj důvod není uveden"),
|
||||
"deleteRequestSLAText": MessageLookupByLibrary.simpleMessage(
|
||||
"Váš požadavek bude zpracován do 72 hodin."),
|
||||
"deleteSharedAlbum":
|
||||
MessageLookupByLibrary.simpleMessage("Opustit sdílené album?"),
|
||||
"deselectAll":
|
||||
MessageLookupByLibrary.simpleMessage("Zrušte výběr všech"),
|
||||
"deviceCodeHint": MessageLookupByLibrary.simpleMessage("Zadejte kód"),
|
||||
"deviceLock": MessageLookupByLibrary.simpleMessage("Zámek zařízení"),
|
||||
"deviceNotFound":
|
||||
MessageLookupByLibrary.simpleMessage("Zařízení nebylo nalezeno"),
|
||||
"didYouKnow": MessageLookupByLibrary.simpleMessage("Věděli jste?"),
|
||||
"discord": MessageLookupByLibrary.simpleMessage("Discord"),
|
||||
"discover_food": MessageLookupByLibrary.simpleMessage("Jídlo"),
|
||||
"discover_pets":
|
||||
MessageLookupByLibrary.simpleMessage("Domácí mazlíčci"),
|
||||
"discover_screenshots":
|
||||
MessageLookupByLibrary.simpleMessage("Snímky obrazovky"),
|
||||
"discover_selfies": MessageLookupByLibrary.simpleMessage("Selfie"),
|
||||
"discover_wallpapers": MessageLookupByLibrary.simpleMessage("Pozadí"),
|
||||
"dismiss": MessageLookupByLibrary.simpleMessage("Zrušit"),
|
||||
"done": MessageLookupByLibrary.simpleMessage("Hotovo"),
|
||||
"doubleYourStorage":
|
||||
MessageLookupByLibrary.simpleMessage("Zdvojnásobte své úložiště"),
|
||||
"download": MessageLookupByLibrary.simpleMessage("Stáhnout"),
|
||||
"downloadFailed":
|
||||
MessageLookupByLibrary.simpleMessage("Stahování selhalo"),
|
||||
"downloading": MessageLookupByLibrary.simpleMessage("Stahuji..."),
|
||||
"edit": MessageLookupByLibrary.simpleMessage("Upravit"),
|
||||
"editLocation": MessageLookupByLibrary.simpleMessage("Upravit polohu"),
|
||||
"editLocationTagTitle":
|
||||
MessageLookupByLibrary.simpleMessage("Upravit lokalitu"),
|
||||
"editPerson": MessageLookupByLibrary.simpleMessage("Upravit osobu"),
|
||||
"editTime": MessageLookupByLibrary.simpleMessage("Upravit čas"),
|
||||
"email": MessageLookupByLibrary.simpleMessage("E-mail"),
|
||||
"emailAlreadyRegistered": MessageLookupByLibrary.simpleMessage(
|
||||
"E-mail je již zaregistrován."),
|
||||
"emailNotRegistered":
|
||||
MessageLookupByLibrary.simpleMessage("E-mail není registrován."),
|
||||
"empty": MessageLookupByLibrary.simpleMessage("Vyprázdnit"),
|
||||
"emptyTrash": MessageLookupByLibrary.simpleMessage("Vyprázdnit koš?"),
|
||||
"enable": MessageLookupByLibrary.simpleMessage("Povolit"),
|
||||
"enabled": MessageLookupByLibrary.simpleMessage("Zapnuto"),
|
||||
"encryption": MessageLookupByLibrary.simpleMessage("Šifrování"),
|
||||
"encryptionKeys":
|
||||
MessageLookupByLibrary.simpleMessage("Šifrovací klíče"),
|
||||
"enterAlbumName":
|
||||
MessageLookupByLibrary.simpleMessage("Zadejte název alba"),
|
||||
"enterDateOfBirth":
|
||||
MessageLookupByLibrary.simpleMessage("Narozeniny (volitelné)"),
|
||||
"enterFileName":
|
||||
MessageLookupByLibrary.simpleMessage("Zadejte název souboru"),
|
||||
"enterPassword": MessageLookupByLibrary.simpleMessage("Zadejte heslo"),
|
||||
"enterPin": MessageLookupByLibrary.simpleMessage("Zadejte PIN"),
|
||||
"enterValidEmail": MessageLookupByLibrary.simpleMessage(
|
||||
"Prosím, zadejte platnou e-mailovou adresu."),
|
||||
"enterYourEmailAddress": MessageLookupByLibrary.simpleMessage(
|
||||
"Zadejte svou e-mailovou adresu"),
|
||||
"enterYourPassword":
|
||||
MessageLookupByLibrary.simpleMessage("Zadejte své heslo"),
|
||||
"enterYourRecoveryKey": MessageLookupByLibrary.simpleMessage(
|
||||
"Zadejte svůj obnovovací klíč"),
|
||||
"everywhere": MessageLookupByLibrary.simpleMessage("všude"),
|
||||
"exif": MessageLookupByLibrary.simpleMessage("EXIF"),
|
||||
"exportLogs": MessageLookupByLibrary.simpleMessage("Exportovat logy"),
|
||||
"exportYourData":
|
||||
MessageLookupByLibrary.simpleMessage("Exportujte svá data"),
|
||||
"faces": MessageLookupByLibrary.simpleMessage("Obličeje"),
|
||||
"failedToDownloadVideo": MessageLookupByLibrary.simpleMessage(
|
||||
"Stahování videa se nezdařilo"),
|
||||
"failedToLoadAlbums":
|
||||
MessageLookupByLibrary.simpleMessage("Nepodařilo se načíst alba"),
|
||||
"failedToPlayVideo": MessageLookupByLibrary.simpleMessage(
|
||||
"Přehrávání videa se nezdařilo"),
|
||||
"familyPlanPortalTitle": MessageLookupByLibrary.simpleMessage("Rodina"),
|
||||
"faq": MessageLookupByLibrary.simpleMessage("Často kladené dotazy"),
|
||||
"faqs": MessageLookupByLibrary.simpleMessage("Často kladené dotazy"),
|
||||
"favorite": MessageLookupByLibrary.simpleMessage("Oblíbené"),
|
||||
"feedback": MessageLookupByLibrary.simpleMessage("Zpětná vazba"),
|
||||
"fileInfoAddDescHint":
|
||||
MessageLookupByLibrary.simpleMessage("Přidat popis..."),
|
||||
"fileTypes": MessageLookupByLibrary.simpleMessage("Typy souboru"),
|
||||
"filesDeleted":
|
||||
MessageLookupByLibrary.simpleMessage("Soubory odstraněny"),
|
||||
"flip": MessageLookupByLibrary.simpleMessage("Překlopit"),
|
||||
"freeUpSpace": MessageLookupByLibrary.simpleMessage("Uvolnit místo"),
|
||||
"gallery": MessageLookupByLibrary.simpleMessage("Galerie"),
|
||||
"general": MessageLookupByLibrary.simpleMessage("Obecné"),
|
||||
"generatingEncryptionKeys": MessageLookupByLibrary.simpleMessage(
|
||||
"Generování šifrovacích klíčů..."),
|
||||
"goToSettings":
|
||||
MessageLookupByLibrary.simpleMessage("Jít do nastavení"),
|
||||
"hidden": MessageLookupByLibrary.simpleMessage("Skryté"),
|
||||
"hide": MessageLookupByLibrary.simpleMessage("Skrýt"),
|
||||
"howItWorks": MessageLookupByLibrary.simpleMessage("Jak to funguje"),
|
||||
"iOSOkButton": MessageLookupByLibrary.simpleMessage("OK"),
|
||||
"ignoreUpdate": MessageLookupByLibrary.simpleMessage("Ignorovat"),
|
||||
"immediately": MessageLookupByLibrary.simpleMessage("Ihned"),
|
||||
"importing": MessageLookupByLibrary.simpleMessage("Importování…"),
|
||||
"incorrectPasswordTitle":
|
||||
MessageLookupByLibrary.simpleMessage("Nesprávné heslo"),
|
||||
"incorrectRecoveryKeyBody": MessageLookupByLibrary.simpleMessage(""),
|
||||
"incorrectRecoveryKeyTitle":
|
||||
MessageLookupByLibrary.simpleMessage("Nesprávný obnovovací klíč"),
|
||||
"info": MessageLookupByLibrary.simpleMessage("Informace"),
|
||||
"installManually":
|
||||
MessageLookupByLibrary.simpleMessage("Instalovat manuálně"),
|
||||
"invalidEmailAddress":
|
||||
MessageLookupByLibrary.simpleMessage("Neplatná e-mailová adresa"),
|
||||
"invalidKey": MessageLookupByLibrary.simpleMessage("Neplatný klíč"),
|
||||
"invite": MessageLookupByLibrary.simpleMessage("Pozvat"),
|
||||
"inviteToEnte": MessageLookupByLibrary.simpleMessage("Pozvat do Ente"),
|
||||
"inviteYourFriendsToEnte":
|
||||
MessageLookupByLibrary.simpleMessage("Pozvěte své přátelé do Ente"),
|
||||
"join": MessageLookupByLibrary.simpleMessage("Připojit se"),
|
||||
"kindlyHelpUsWithThisInformation": MessageLookupByLibrary.simpleMessage(
|
||||
"Pomozte nám s těmito informacemi"),
|
||||
"language": MessageLookupByLibrary.simpleMessage("Jazyk"),
|
||||
"leave": MessageLookupByLibrary.simpleMessage("Odejít"),
|
||||
"leaveAlbum": MessageLookupByLibrary.simpleMessage("Opustit album"),
|
||||
"left": MessageLookupByLibrary.simpleMessage("Doleva"),
|
||||
"lightTheme": MessageLookupByLibrary.simpleMessage("Světlý"),
|
||||
"linkExpiresOn": m46,
|
||||
"linkHasExpired":
|
||||
MessageLookupByLibrary.simpleMessage("Platnost odkazu vypršela"),
|
||||
"linkNeverExpires": MessageLookupByLibrary.simpleMessage("Nikdy"),
|
||||
@@ -140,13 +297,18 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("Načítání galerie..."),
|
||||
"location": MessageLookupByLibrary.simpleMessage("Poloha"),
|
||||
"locations": MessageLookupByLibrary.simpleMessage("Lokality"),
|
||||
"lockButtonLabel": MessageLookupByLibrary.simpleMessage("Uzamknout"),
|
||||
"logInLabel": MessageLookupByLibrary.simpleMessage("Přihlásit se"),
|
||||
"loggingOut": MessageLookupByLibrary.simpleMessage("Odhlašování..."),
|
||||
"loginSessionExpired":
|
||||
MessageLookupByLibrary.simpleMessage("Relace vypršela"),
|
||||
"loginWithTOTP":
|
||||
MessageLookupByLibrary.simpleMessage("Přihlášení pomocí TOTP"),
|
||||
"logout": MessageLookupByLibrary.simpleMessage("Odhlásit se"),
|
||||
"lostDevice":
|
||||
MessageLookupByLibrary.simpleMessage("Ztratili jste zařízení?"),
|
||||
"manage": MessageLookupByLibrary.simpleMessage("Spravovat"),
|
||||
"manageParticipants": MessageLookupByLibrary.simpleMessage("Spravovat"),
|
||||
"manageSubscription":
|
||||
MessageLookupByLibrary.simpleMessage("Spravovat předplatné"),
|
||||
"map": MessageLookupByLibrary.simpleMessage("Mapa"),
|
||||
@@ -154,13 +316,21 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"mastodon": MessageLookupByLibrary.simpleMessage("Mastodon"),
|
||||
"matrix": MessageLookupByLibrary.simpleMessage("Matrix"),
|
||||
"me": MessageLookupByLibrary.simpleMessage("Já"),
|
||||
"merchandise": MessageLookupByLibrary.simpleMessage("E-shop"),
|
||||
"mergedPhotos":
|
||||
MessageLookupByLibrary.simpleMessage("Sloučené fotografie"),
|
||||
"moments": MessageLookupByLibrary.simpleMessage("Momenty"),
|
||||
"monthly": MessageLookupByLibrary.simpleMessage("Měsíčně"),
|
||||
"moreDetails":
|
||||
MessageLookupByLibrary.simpleMessage("Další podrobnosti"),
|
||||
"moveToAlbum":
|
||||
MessageLookupByLibrary.simpleMessage("Přesunout do alba"),
|
||||
"movedToTrash":
|
||||
MessageLookupByLibrary.simpleMessage("Přesunuto do koše"),
|
||||
"never": MessageLookupByLibrary.simpleMessage("Nikdy"),
|
||||
"newAlbum": MessageLookupByLibrary.simpleMessage("Nové album"),
|
||||
"newPerson": MessageLookupByLibrary.simpleMessage("Nová osoba"),
|
||||
"newRange": MessageLookupByLibrary.simpleMessage("Nový rozsah"),
|
||||
"newest": MessageLookupByLibrary.simpleMessage("Nejnovější"),
|
||||
"next": MessageLookupByLibrary.simpleMessage("Další"),
|
||||
"no": MessageLookupByLibrary.simpleMessage("Ne"),
|
||||
@@ -170,86 +340,203 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("✨ Žádné duplicity"),
|
||||
"noInternetConnection":
|
||||
MessageLookupByLibrary.simpleMessage("Žádné připojení k internetu"),
|
||||
"noPhotosFoundHere": MessageLookupByLibrary.simpleMessage(
|
||||
"Zde nebyly nalezeny žádné fotky"),
|
||||
"noRecoveryKey":
|
||||
MessageLookupByLibrary.simpleMessage("Nemáte obnovovací klíč?"),
|
||||
"noResultsFound": MessageLookupByLibrary.simpleMessage(
|
||||
"Nebyly nalezeny žádné výsledky"),
|
||||
"notifications": MessageLookupByLibrary.simpleMessage("Notifikace"),
|
||||
"ok": MessageLookupByLibrary.simpleMessage("Ok"),
|
||||
"onDevice": MessageLookupByLibrary.simpleMessage("V zařízení"),
|
||||
"onEnte": MessageLookupByLibrary.simpleMessage(
|
||||
"Na <branding>ente</branding>"),
|
||||
"onThisDay": MessageLookupByLibrary.simpleMessage("On this day"),
|
||||
"oops": MessageLookupByLibrary.simpleMessage("Jejda"),
|
||||
"oopsSomethingWentWrong":
|
||||
MessageLookupByLibrary.simpleMessage("Jejda, něco se pokazilo"),
|
||||
"openAlbumInBrowser":
|
||||
MessageLookupByLibrary.simpleMessage("Otevřít album v prohlížeči"),
|
||||
"openFile": MessageLookupByLibrary.simpleMessage("Otevřít soubor"),
|
||||
"openSettings":
|
||||
MessageLookupByLibrary.simpleMessage("Otevřít Nastavení"),
|
||||
"pair": MessageLookupByLibrary.simpleMessage("Spárovat"),
|
||||
"panorama": MessageLookupByLibrary.simpleMessage("Panorama"),
|
||||
"password": MessageLookupByLibrary.simpleMessage("Heslo"),
|
||||
"passwordChangedSuccessfully":
|
||||
MessageLookupByLibrary.simpleMessage("Heslo úspěšně změněno"),
|
||||
"paymentDetails":
|
||||
MessageLookupByLibrary.simpleMessage("Platební údaje"),
|
||||
"people": MessageLookupByLibrary.simpleMessage("Lidé"),
|
||||
"permanentlyDelete":
|
||||
MessageLookupByLibrary.simpleMessage("Trvale odstranit"),
|
||||
"personName": MessageLookupByLibrary.simpleMessage("Jméno osoby"),
|
||||
"photos": MessageLookupByLibrary.simpleMessage("Fotky"),
|
||||
"pinAlbum": MessageLookupByLibrary.simpleMessage("Připnout album"),
|
||||
"pleaseLoginAgain":
|
||||
MessageLookupByLibrary.simpleMessage("Přihlaste se, prosím, znovu"),
|
||||
"pleaseTryAgain":
|
||||
MessageLookupByLibrary.simpleMessage("Zkuste to prosím znovu"),
|
||||
"pleaseWait": MessageLookupByLibrary.simpleMessage("Čekejte prosím..."),
|
||||
"previous": MessageLookupByLibrary.simpleMessage("Předchozí"),
|
||||
"privacy": MessageLookupByLibrary.simpleMessage("Soukromí"),
|
||||
"processing": MessageLookupByLibrary.simpleMessage("Zpracovává se"),
|
||||
"publicLinkCreated":
|
||||
MessageLookupByLibrary.simpleMessage("Veřejný odkaz vytvořen"),
|
||||
"queued": MessageLookupByLibrary.simpleMessage("Ve frontě"),
|
||||
"radius": MessageLookupByLibrary.simpleMessage("Rádius"),
|
||||
"rateUs": MessageLookupByLibrary.simpleMessage("Ohodnoť nás"),
|
||||
"rateUsOnStore": m67,
|
||||
"recoverButton": MessageLookupByLibrary.simpleMessage("Obnovit"),
|
||||
"recoveryKeyVerified":
|
||||
MessageLookupByLibrary.simpleMessage("Obnovovací klíč byl ověřen"),
|
||||
"recoverySuccessful":
|
||||
MessageLookupByLibrary.simpleMessage("Úspěšně obnoveno!"),
|
||||
"reddit": MessageLookupByLibrary.simpleMessage("Reddit"),
|
||||
"reenterPin": MessageLookupByLibrary.simpleMessage("Zadejte PIN znovu"),
|
||||
"remove": MessageLookupByLibrary.simpleMessage("Odstranit"),
|
||||
"removeDuplicates":
|
||||
MessageLookupByLibrary.simpleMessage("Odstranit duplicity"),
|
||||
"removeFromAlbum":
|
||||
MessageLookupByLibrary.simpleMessage("Odstranit z alba"),
|
||||
"removeFromAlbumTitle":
|
||||
MessageLookupByLibrary.simpleMessage("Odstranit z alba?"),
|
||||
"removeInvite":
|
||||
MessageLookupByLibrary.simpleMessage("Odstranit pozvání"),
|
||||
"removeLink": MessageLookupByLibrary.simpleMessage("Odstranit odkaz"),
|
||||
"removeParticipant":
|
||||
MessageLookupByLibrary.simpleMessage("Odebrat účastníka"),
|
||||
"removePublicLink":
|
||||
MessageLookupByLibrary.simpleMessage("Odstranit veřejný odkaz"),
|
||||
"removeWithQuestionMark":
|
||||
MessageLookupByLibrary.simpleMessage("Odstranit?"),
|
||||
"rename": MessageLookupByLibrary.simpleMessage("Přejmenovat"),
|
||||
"renameAlbum":
|
||||
MessageLookupByLibrary.simpleMessage("Přejmenovat album"),
|
||||
"renameFile":
|
||||
MessageLookupByLibrary.simpleMessage("Přejmenovat soubor"),
|
||||
"renewsOn": m74,
|
||||
"reportABug": MessageLookupByLibrary.simpleMessage("Nahlásit chybu"),
|
||||
"reportBug": MessageLookupByLibrary.simpleMessage("Nahlásit chybu"),
|
||||
"resendEmail":
|
||||
MessageLookupByLibrary.simpleMessage("Znovu odeslat e-mail"),
|
||||
"resetPasswordTitle":
|
||||
MessageLookupByLibrary.simpleMessage("Obnovit heslo"),
|
||||
"resetPerson": MessageLookupByLibrary.simpleMessage("Odstranit"),
|
||||
"restore": MessageLookupByLibrary.simpleMessage("Obnovit"),
|
||||
"restoringFiles":
|
||||
MessageLookupByLibrary.simpleMessage("Obnovuji soubory..."),
|
||||
"retry": MessageLookupByLibrary.simpleMessage("Opakovat"),
|
||||
"right": MessageLookupByLibrary.simpleMessage("Doprava"),
|
||||
"rotate": MessageLookupByLibrary.simpleMessage("Otočit"),
|
||||
"rotateLeft": MessageLookupByLibrary.simpleMessage("Otočit doleva"),
|
||||
"rotateRight": MessageLookupByLibrary.simpleMessage("Otočit doprava"),
|
||||
"safelyStored":
|
||||
MessageLookupByLibrary.simpleMessage("Bezpečně uloženo"),
|
||||
"save": MessageLookupByLibrary.simpleMessage("Uložit"),
|
||||
"saveCopy": MessageLookupByLibrary.simpleMessage("Uložit kopii"),
|
||||
"saveKey": MessageLookupByLibrary.simpleMessage("Uložit klíč"),
|
||||
"savePerson": MessageLookupByLibrary.simpleMessage("Uložit osobu"),
|
||||
"search": MessageLookupByLibrary.simpleMessage("Hledat"),
|
||||
"searchAlbumsEmptySection":
|
||||
MessageLookupByLibrary.simpleMessage("Alba"),
|
||||
"searchByAlbumNameHint":
|
||||
MessageLookupByLibrary.simpleMessage("Název alba"),
|
||||
"security": MessageLookupByLibrary.simpleMessage("Zabezpečení"),
|
||||
"selectALocation":
|
||||
MessageLookupByLibrary.simpleMessage("Vybrat polohu"),
|
||||
"selectALocationFirst":
|
||||
MessageLookupByLibrary.simpleMessage("Nejprve vyberte polohu"),
|
||||
"selectAlbum": MessageLookupByLibrary.simpleMessage("Vybrat album"),
|
||||
"selectAll": MessageLookupByLibrary.simpleMessage("Vybrat vše"),
|
||||
"selectDate": MessageLookupByLibrary.simpleMessage("Vybrat datum"),
|
||||
"selectFoldersForBackup": MessageLookupByLibrary.simpleMessage(
|
||||
"Vyberte složky pro zálohování"),
|
||||
"selectLanguage": MessageLookupByLibrary.simpleMessage("Vybrat jazyk"),
|
||||
"selectReason": MessageLookupByLibrary.simpleMessage("Vyberte důvod"),
|
||||
"selectTime": MessageLookupByLibrary.simpleMessage("Vybrat čas"),
|
||||
"selectYourPlan":
|
||||
MessageLookupByLibrary.simpleMessage("Vyberte svůj plán"),
|
||||
"selfiesWithThem": m80,
|
||||
"send": MessageLookupByLibrary.simpleMessage("Odeslat"),
|
||||
"sendEmail": MessageLookupByLibrary.simpleMessage("Odeslat e-mail"),
|
||||
"sendInvite": MessageLookupByLibrary.simpleMessage("Odeslat pozvánku"),
|
||||
"sendLink": MessageLookupByLibrary.simpleMessage("Odeslat odkaz"),
|
||||
"sessionExpired":
|
||||
MessageLookupByLibrary.simpleMessage("Relace vypršela"),
|
||||
"setAPassword": MessageLookupByLibrary.simpleMessage("Nastavit heslo"),
|
||||
"setLabel": MessageLookupByLibrary.simpleMessage("Nastavit"),
|
||||
"setNewPassword":
|
||||
MessageLookupByLibrary.simpleMessage("Nastavit nové heslo"),
|
||||
"setNewPin": MessageLookupByLibrary.simpleMessage("Nastavit nový PIN"),
|
||||
"share": MessageLookupByLibrary.simpleMessage("Sdílet"),
|
||||
"shareLink": MessageLookupByLibrary.simpleMessage("Sdílet odkaz"),
|
||||
"sharedByMe": MessageLookupByLibrary.simpleMessage("Sdíleno mnou"),
|
||||
"sharedByYou": MessageLookupByLibrary.simpleMessage("Sdíleno vámi"),
|
||||
"sharedWithMe": MessageLookupByLibrary.simpleMessage("Sdíleno se mnou"),
|
||||
"sharedWithYou": MessageLookupByLibrary.simpleMessage("Sdíleno s vámi"),
|
||||
"sharing": MessageLookupByLibrary.simpleMessage("Sdílení..."),
|
||||
"skip": MessageLookupByLibrary.simpleMessage("Přeskočit"),
|
||||
"sorry": MessageLookupByLibrary.simpleMessage("Omlouváme se"),
|
||||
"sort": MessageLookupByLibrary.simpleMessage("Seřadit"),
|
||||
"sortAlbumsBy": MessageLookupByLibrary.simpleMessage("Seřadit podle"),
|
||||
"sortNewestFirst":
|
||||
MessageLookupByLibrary.simpleMessage("Od nejnovějších"),
|
||||
"sortOldestFirst":
|
||||
MessageLookupByLibrary.simpleMessage("Od nejstarších"),
|
||||
"stopCastingTitle":
|
||||
MessageLookupByLibrary.simpleMessage("Zastavit přenos"),
|
||||
"storage": MessageLookupByLibrary.simpleMessage("Úložiště"),
|
||||
"storageBreakupFamily": MessageLookupByLibrary.simpleMessage("Rodina"),
|
||||
"strongStrength": MessageLookupByLibrary.simpleMessage("Silné"),
|
||||
"subscribe": MessageLookupByLibrary.simpleMessage("Odebírat"),
|
||||
"subscription": MessageLookupByLibrary.simpleMessage("Předplatné"),
|
||||
"suggestFeatures":
|
||||
MessageLookupByLibrary.simpleMessage("Navrhnout funkce"),
|
||||
"support": MessageLookupByLibrary.simpleMessage("Podpora"),
|
||||
"syncStopped":
|
||||
MessageLookupByLibrary.simpleMessage("Synchronizace zastavena"),
|
||||
"syncing": MessageLookupByLibrary.simpleMessage("Synchronizace..."),
|
||||
"systemTheme": MessageLookupByLibrary.simpleMessage("Systém"),
|
||||
"terminate": MessageLookupByLibrary.simpleMessage("Ukončit"),
|
||||
"terminateSession":
|
||||
MessageLookupByLibrary.simpleMessage("Ukončit relaci?"),
|
||||
"terms": MessageLookupByLibrary.simpleMessage("Podmínky"),
|
||||
"thankYou": MessageLookupByLibrary.simpleMessage("Děkujeme"),
|
||||
"theDownloadCouldNotBeCompleted": MessageLookupByLibrary.simpleMessage(
|
||||
"Stahování nebylo možné dokončit"),
|
||||
"theme": MessageLookupByLibrary.simpleMessage("Motiv"),
|
||||
"thisDevice": MessageLookupByLibrary.simpleMessage("Toto zařízení"),
|
||||
"thisIsMeExclamation":
|
||||
MessageLookupByLibrary.simpleMessage("To jsem já!"),
|
||||
"totalSize": MessageLookupByLibrary.simpleMessage("Celková velikost"),
|
||||
"trash": MessageLookupByLibrary.simpleMessage("Koš"),
|
||||
"tryAgain": MessageLookupByLibrary.simpleMessage("Zkusit znovu"),
|
||||
"twitter": MessageLookupByLibrary.simpleMessage("Twitter"),
|
||||
"unlock": MessageLookupByLibrary.simpleMessage("Odemknout"),
|
||||
"unpinAlbum": MessageLookupByLibrary.simpleMessage("Odepnout album"),
|
||||
"unselectAll": MessageLookupByLibrary.simpleMessage("Zrušit výběr"),
|
||||
"update": MessageLookupByLibrary.simpleMessage("Aktualizovat"),
|
||||
"updateAvailable":
|
||||
MessageLookupByLibrary.simpleMessage("Je k dispozici aktualizace"),
|
||||
"upgrade": MessageLookupByLibrary.simpleMessage("Upgradovat"),
|
||||
"uploadingFilesToAlbum": MessageLookupByLibrary.simpleMessage(
|
||||
"Nahrávání souborů do alba..."),
|
||||
"usedSpace": MessageLookupByLibrary.simpleMessage("Využité místo"),
|
||||
"verify": MessageLookupByLibrary.simpleMessage("Ověřit"),
|
||||
"verifyEmail": MessageLookupByLibrary.simpleMessage("Ověřit e-mail"),
|
||||
"verifyEmailID": m109,
|
||||
"verifyIDLabel": MessageLookupByLibrary.simpleMessage("Ověřit"),
|
||||
"verifying": MessageLookupByLibrary.simpleMessage("Ověřování..."),
|
||||
"verifyingRecoveryKey": MessageLookupByLibrary.simpleMessage(
|
||||
"Ověřování obnovovacího klíče..."),
|
||||
"videos": MessageLookupByLibrary.simpleMessage("Videa"),
|
||||
"viewAll": MessageLookupByLibrary.simpleMessage("Zobrazit vše"),
|
||||
"viewer": MessageLookupByLibrary.simpleMessage("Prohlížející"),
|
||||
"warning": MessageLookupByLibrary.simpleMessage("Varování"),
|
||||
"weAreOpenSource":
|
||||
MessageLookupByLibrary.simpleMessage("Jsme open source!"),
|
||||
"weakStrength": MessageLookupByLibrary.simpleMessage("Slabé"),
|
||||
"welcomeBack": MessageLookupByLibrary.simpleMessage("Vítejte zpět!"),
|
||||
"whatsNew": MessageLookupByLibrary.simpleMessage("Co je nového"),
|
||||
"yearly": MessageLookupByLibrary.simpleMessage("Ročně"),
|
||||
@@ -258,9 +545,13 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"yesDelete": MessageLookupByLibrary.simpleMessage("Ano, smazat"),
|
||||
"yesDiscardChanges":
|
||||
MessageLookupByLibrary.simpleMessage("Ano, zahodit změny"),
|
||||
"yesLogout": MessageLookupByLibrary.simpleMessage("Ano, odhlásit se"),
|
||||
"yesRemove": MessageLookupByLibrary.simpleMessage("Ano, odstranit"),
|
||||
"yesRenew": MessageLookupByLibrary.simpleMessage("Ano, obnovit"),
|
||||
"you": MessageLookupByLibrary.simpleMessage("Vy"),
|
||||
"youAndThem": m114,
|
||||
"yourAccountHasBeenDeleted":
|
||||
MessageLookupByLibrary.simpleMessage("Váš účet byl smazán"),
|
||||
"yourMap": MessageLookupByLibrary.simpleMessage("Vaše mapa")
|
||||
};
|
||||
}
|
||||
|
||||
33
mobile/lib/generated/intl/messages_da.dart
generated
@@ -29,24 +29,24 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
static String m24(supportEmail) =>
|
||||
"Send venligst en email til ${supportEmail} fra din registrerede email adresse";
|
||||
|
||||
static String m35(storageAmountInGB) =>
|
||||
static String m36(storageAmountInGB) =>
|
||||
"${storageAmountInGB} GB hver gang nogen tilmelder sig et betalt abonnement og anvender din kode";
|
||||
|
||||
static String m45(expiryTime) => "Link udløber den ${expiryTime}";
|
||||
static String m46(expiryTime) => "Link udløber den ${expiryTime}";
|
||||
|
||||
static String m55(passwordStrengthValue) =>
|
||||
static String m56(passwordStrengthValue) =>
|
||||
"Kodeordets styrke: ${passwordStrengthValue}";
|
||||
|
||||
static String m77(count) => "${count} valgt";
|
||||
static String m78(count) => "${count} valgt";
|
||||
|
||||
static String m81(verificationID) =>
|
||||
static String m82(verificationID) =>
|
||||
"Hey, kan du bekræfte, at dette er dit ente.io verifikation ID: ${verificationID}";
|
||||
|
||||
static String m90(storageAmountInGB) => "${storageAmountInGB} GB";
|
||||
static String m91(storageAmountInGB) => "${storageAmountInGB} GB";
|
||||
|
||||
static String m96(storageAmountInGB) => "De får også ${storageAmountInGB} GB";
|
||||
static String m97(storageAmountInGB) => "De får også ${storageAmountInGB} GB";
|
||||
|
||||
static String m110(email) =>
|
||||
static String m112(email) =>
|
||||
"Vi har sendt en email til <green>${email}</green>";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
@@ -219,7 +219,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("Find folk hurtigt ved navn"),
|
||||
"forgotPassword":
|
||||
MessageLookupByLibrary.simpleMessage("Glemt adgangskode"),
|
||||
"freeStorageOnReferralSuccess": m35,
|
||||
"freeStorageOnReferralSuccess": m36,
|
||||
"freeUpDeviceSpace":
|
||||
MessageLookupByLibrary.simpleMessage("Frigør enhedsplads"),
|
||||
"freeUpDeviceSpaceDesc": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -253,7 +253,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("Enheds grænse"),
|
||||
"linkEnabled": MessageLookupByLibrary.simpleMessage("Aktiveret"),
|
||||
"linkExpired": MessageLookupByLibrary.simpleMessage("Udløbet"),
|
||||
"linkExpiresOn": m45,
|
||||
"linkExpiresOn": m46,
|
||||
"linkExpiry": MessageLookupByLibrary.simpleMessage("Udløb af link"),
|
||||
"linkHasExpired":
|
||||
MessageLookupByLibrary.simpleMessage("Linket er udløbet"),
|
||||
@@ -294,6 +294,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"noRecoveryKey":
|
||||
MessageLookupByLibrary.simpleMessage("Ingen gendannelsesnøgle?"),
|
||||
"ok": MessageLookupByLibrary.simpleMessage("Ok"),
|
||||
"onThisDay": MessageLookupByLibrary.simpleMessage("On this day"),
|
||||
"oops": MessageLookupByLibrary.simpleMessage("Ups"),
|
||||
"oopsSomethingWentWrong":
|
||||
MessageLookupByLibrary.simpleMessage("Ups, noget gik galt"),
|
||||
@@ -303,7 +304,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"passwordChangedSuccessfully": MessageLookupByLibrary.simpleMessage(
|
||||
"Adgangskoden er blevet ændret"),
|
||||
"passwordLock": MessageLookupByLibrary.simpleMessage("Adgangskodelås"),
|
||||
"passwordStrength": m55,
|
||||
"passwordStrength": m56,
|
||||
"passwordWarning": MessageLookupByLibrary.simpleMessage(
|
||||
"Vi gemmer ikke denne adgangskode, så hvis du glemmer den <underline>kan vi ikke dekryptere dine data</underline>"),
|
||||
"pendingItems":
|
||||
@@ -375,7 +376,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"selectedFoldersWillBeEncryptedAndBackedUp":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Valgte mapper vil blive krypteret og sikkerhedskopieret"),
|
||||
"selectedPhotos": m77,
|
||||
"selectedPhotos": m78,
|
||||
"sendEmail": MessageLookupByLibrary.simpleMessage("Send email"),
|
||||
"sendLink": MessageLookupByLibrary.simpleMessage("Send link"),
|
||||
"setPasswordTitle":
|
||||
@@ -383,7 +384,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"setupComplete":
|
||||
MessageLookupByLibrary.simpleMessage("Opsætning fuldført"),
|
||||
"shareALink": MessageLookupByLibrary.simpleMessage("Del et link"),
|
||||
"shareTextConfirmOthersVerificationID": m81,
|
||||
"shareTextConfirmOthersVerificationID": m82,
|
||||
"shareWithNonenteUsers":
|
||||
MessageLookupByLibrary.simpleMessage("Del med ikke Ente brugere"),
|
||||
"showMemories": MessageLookupByLibrary.simpleMessage("Vis minder"),
|
||||
@@ -403,7 +404,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Beklager, vi kunne ikke generere sikre krypteringsnøgler på denne enhed.\n\nForsøg venligst at oprette en konto fra en anden enhed."),
|
||||
"status": MessageLookupByLibrary.simpleMessage("Status"),
|
||||
"storageInGB": m90,
|
||||
"storageInGB": m91,
|
||||
"strongStrength": MessageLookupByLibrary.simpleMessage("Stærkt"),
|
||||
"subscribe": MessageLookupByLibrary.simpleMessage("Abonner"),
|
||||
"subscribeToEnableSharing": MessageLookupByLibrary.simpleMessage(
|
||||
@@ -417,7 +418,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("Afslut session?"),
|
||||
"termsOfServicesTitle":
|
||||
MessageLookupByLibrary.simpleMessage("Betingelser"),
|
||||
"theyAlsoGetXGb": m96,
|
||||
"theyAlsoGetXGb": m97,
|
||||
"thisCanBeUsedToRecoverYourAccountIfYou":
|
||||
MessageLookupByLibrary.simpleMessage(
|
||||
"Dette kan bruges til at gendanne din konto, hvis du mister din anden faktor"),
|
||||
@@ -456,7 +457,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"viewer": MessageLookupByLibrary.simpleMessage("Seer"),
|
||||
"waitingForWifi":
|
||||
MessageLookupByLibrary.simpleMessage("Venter på Wi-fi..."),
|
||||
"weHaveSendEmailTo": m110,
|
||||
"weHaveSendEmailTo": m112,
|
||||
"weakStrength": MessageLookupByLibrary.simpleMessage("Svagt"),
|
||||
"welcomeBack":
|
||||
MessageLookupByLibrary.simpleMessage("Velkommen tilbage!"),
|
||||
|
||||