Compare commits
77 Commits
usearch_up
...
rust_proce
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a318977001 | ||
|
|
eff471c739 | ||
|
|
5454b262a4 | ||
|
|
e9ef9d55a4 | ||
|
|
968f04c04a | ||
|
|
59cb3f091e | ||
|
|
630f5a2706 | ||
|
|
4a743be322 | ||
|
|
c2db1f7da9 | ||
|
|
843e956a8a | ||
|
|
c2d1c66888 | ||
|
|
e2aabfb95a | ||
|
|
dbf88c7bed | ||
|
|
a06a5be983 | ||
|
|
3bba125f1c | ||
|
|
1718e5d1d6 | ||
|
|
b16c9af36b | ||
|
|
1cc3499019 | ||
|
|
4260c3c769 | ||
|
|
209291e09a | ||
|
|
dd08ca82fe | ||
|
|
8d71a6bb58 | ||
|
|
c583fa4742 | ||
|
|
ec0d3c4266 | ||
|
|
55cc92e57d | ||
|
|
3f71d491e9 | ||
|
|
304daf0b09 | ||
|
|
e1281657ba | ||
|
|
595871f571 | ||
|
|
d31127c2e3 | ||
|
|
09d7b82c08 | ||
|
|
88c9f4943b | ||
|
|
cacc7dc85a | ||
|
|
24a30709cd | ||
|
|
8e4e06af73 | ||
|
|
439b4fdeec | ||
|
|
32efdf464e | ||
|
|
f76fa34e5b | ||
|
|
868c45baa4 | ||
|
|
fa1838c82e | ||
|
|
6b5db8d85b | ||
|
|
9c071c0dab | ||
|
|
73b87950de | ||
|
|
ee0c7472a1 | ||
|
|
ba56908d2d | ||
|
|
b5d725e139 | ||
|
|
5750d72c5a | ||
|
|
00a430927f | ||
|
|
ab57a1f8fe | ||
|
|
cfdeb475ef | ||
|
|
1f0f240f97 | ||
|
|
2ff5058a3e | ||
|
|
641dfdd11e | ||
|
|
054ad8b480 | ||
|
|
b3827dd812 | ||
|
|
087ba629e0 | ||
|
|
e5e86fb41a | ||
|
|
1e9cc64a64 | ||
|
|
0205bec30a | ||
|
|
fad0c4559f | ||
|
|
2e4866d302 | ||
|
|
aadbe75c50 | ||
|
|
899bf79460 | ||
|
|
21af6d0070 | ||
|
|
1bad2b3555 | ||
|
|
ffa50df43e | ||
|
|
8fdc7dcd89 | ||
|
|
1ed26567a5 | ||
|
|
6101570c9d | ||
|
|
a33bbb22ae | ||
|
|
a2661ef6ed | ||
|
|
8daa22e423 | ||
|
|
aeb2235875 | ||
|
|
bf903562f6 | ||
|
|
9cb7c01481 | ||
|
|
233d1715e9 | ||
|
|
e3c019f7ed |
27
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
@@ -1,39 +1,22 @@
|
||||
name: Report a bug
|
||||
description: For regressions only (things that were working earlier)
|
||||
description: Things that were working earlier but don't anymore
|
||||
labels: []
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Before opening a new issue, **please** ensure
|
||||
1. You are on the latest version,
|
||||
2. You've searched for existing issues,
|
||||
3. It was working earlier (otherwise use [this](https://github.com/ente-io/ente/discussions/categories/enhancements))
|
||||
4. It is not about self hosting (otherwise use [this](https://github.com/ente-io/ente/discussions/categories/q-a))
|
||||
**Checklist**
|
||||
1. You've searched existing [issues](https://github.com/search?q=repo%3Aente-io%2Fente+&type=issues) and [discussions](https://github.com/search?q=repo%3Aente-io%2Fente+&type=discussions)
|
||||
2. It was working earlier (otherwise use [enhancements](https://github.com/ente-io/ente/discussions/categories/enhancements))
|
||||
3. It is not about self hosting (for those use [this](https://github.com/ente-io/ente/discussions/categories/q-a))
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: >
|
||||
Describe the bug and steps to reproduce the behaviour, and how it
|
||||
differs from the previously working behaviour.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Version
|
||||
description: The version can be seen at the bottom of settings.
|
||||
placeholder: e.g. v1.2.3
|
||||
- type: input
|
||||
attributes:
|
||||
label: Last working version
|
||||
description: >
|
||||
The version where things were last known to be working. It is fine
|
||||
if you don't remember the exact version (mention roughly then),
|
||||
but **if there just isn't a last working version, then please file
|
||||
it as an
|
||||
[enhancement](https://github.com/ente-io/ente/discussions/categories/enhancements))**
|
||||
(where the community upvotes can be used to help prioritize).
|
||||
placeholder: e.g. v1.2.3
|
||||
- type: dropdown
|
||||
attributes:
|
||||
label: What product are you using?
|
||||
|
||||
2
.github/workflows/auth-release.yml
vendored
@@ -98,7 +98,7 @@ jobs:
|
||||
|
||||
- name: Install appimagetool
|
||||
run: |
|
||||
wget -O appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage"
|
||||
wget -O appimagetool "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
|
||||
chmod +x appimagetool
|
||||
mv appimagetool /usr/local/bin/
|
||||
|
||||
|
||||
@@ -1,54 +1,42 @@
|
||||
# Contributing
|
||||
|
||||
First and foremost, thank you for your interest in contributing to Ente 🙏
|
||||
|
||||
There are many ways to contribute, and most of them don't require writing code.
|
||||
|
||||
* [Spread the word](#spread-the-word)
|
||||
* [Engage with the community](#engage-with-the-community)
|
||||
* [Translate](#translate)
|
||||
* [Document](#document)
|
||||
|
||||
- [Spread the word](#spread-the-word)
|
||||
- [Engage with the community](#engage-with-the-community)
|
||||
- [Translate](#translate)
|
||||
- [Document](#document)
|
||||
|
||||
## Spread the word
|
||||
|
||||
This is perhaps the most impactful contribution you can make. [Spread the
|
||||
word](https://help.ente.io/photos/features/referral-program/). Online on your
|
||||
favorite social media channels. Offline to your friends and family who are
|
||||
looking for a privacy-friendly alternative to big tech.
|
||||
**This is the most impactful contribution you can make**.
|
||||
|
||||
[Spread the word](https://help.ente.io/photos/features/referral-program/). Online on your favorite social media channels. Offline to your friends and family who are looking for a privacy-friendly alternative to big tech.
|
||||
|
||||
## Engage with the community
|
||||
|
||||
Just hang around, enjoy the vibe. Answer someone's query on our
|
||||
[Discord](https://discord.gg/z2YVKkycX3), or pile on in the sporadic #off-topic
|
||||
rants there. Chuckle (or wince!) at our [Twitter](https://twitter.com/enteio)
|
||||
memes. Suggest a new feature in our [Github
|
||||
Discussions](https://github.com/ente-io/ente/discussions/new?category=enhancements),
|
||||
or upvote the existing ones that you feel we should focus on first. Provide your
|
||||
opinion on existing threads.
|
||||
Just hang around, enjoy the vibe. The Ente community — the people who are building Ente, and the people who are using Ente — hang out at various places depending on their proclivity:
|
||||
|
||||
These might seem like small things, but it provides us energy. Knowing that
|
||||
there is a community of people who care for what we are building.
|
||||
- [Discord](https://discord.ente.io)
|
||||
- [Mastodon](https://fosstodon.org/@ente)
|
||||
- [X / Twitter](https://twitter.com/enteio)
|
||||
- [Github Discussions](https://github.com/ente-io/ente/discussions)
|
||||
|
||||
Just being around might seem a small thing, but it provides us energy. Knowing that there is a community of people who care for what we are building, **who want us to do better**.
|
||||
|
||||
## Translate
|
||||
|
||||
If you're interested in helping out with translation, please visit our Crowdin
|
||||
projects to get started:
|
||||
|
||||
| Project | |
|
||||
| ------------- | ------------- |
|
||||
| [Auth](https://crowdin.com/project/ente-authenticator-app) | [](https://crowdin.com/project/ente-authenticator-app) |
|
||||
| [Photos](https://crowdin.com/project/ente-photos-app) | [](https://crowdin.com/project/ente-photos-app) |
|
||||
| [Photos Web / Desktop](https://crowdin.com/project/ente-photos-web) | [](https://crowdin.com/project/ente-photos-web) |
|
||||
Visit our Crowdin projects to help with translations:
|
||||
|
||||
| Project | |
|
||||
| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Auth](https://crowdin.com/project/ente-authenticator-app) | [](https://crowdin.com/project/ente-authenticator-app) |
|
||||
| [Photos](https://crowdin.com/project/ente-photos-app) | [](https://crowdin.com/project/ente-photos-app) |
|
||||
| [Photos Web / Desktop](https://crowdin.com/project/ente-photos-web) | [](https://crowdin.com/project/ente-photos-web) |
|
||||
|
||||
If your language is not listed for translation, please [create a GitHub
|
||||
issue](https://github.com/ente-io/ente/issues/new?title=Request+for+New+Language+Translation&body=Language+name%3A+%0AProject%3A+auth%2Fphotos%2Fboth)
|
||||
to have it added. It is okay to have partial translations. Once ~90% of the
|
||||
strings in a language get translated, we will start surfacing it in the apps.
|
||||
|
||||
Thank you for your support.
|
||||
|
||||
## Document
|
||||
|
||||
The help guides and FAQs for users of Ente products are also open source, and
|
||||
@@ -60,25 +48,9 @@ See [docs/](docs/README.md) for how to edit these documents.
|
||||
|
||||
## Code contributions
|
||||
|
||||
Code is a small aspect of community, and the ways mentioned above are more
|
||||
important in helping us. But if you'd _really_ like to contribute code, it is
|
||||
best to start small. Consider some well-scoped changes, say like adding more
|
||||
[custom icons to auth](auth/docs/adding-icons.md).
|
||||
If you'd like to contribute code, it is best to start small. Consider some well-scoped changes, say like adding more [custom icons to auth](auth/docs/adding-icons.md), or fixing a specific bug.
|
||||
|
||||
Each of the individual product/platform specific directories in this repository
|
||||
have instructions on setting up a dev environment.
|
||||
|
||||
For anything beyond trivial bug fixes, please use
|
||||
[discussions](https://github.com/ente-io/ente/discussions) instead of performing
|
||||
code changes directly.
|
||||
|
||||
> [!TIP]
|
||||
>
|
||||
> Please remember that code is a important, but small, part of the overall big
|
||||
> picture that makes a product a joy to use. Something that's easy in code is
|
||||
> not necessarily the right choice for the product as a whole. So we'll repeat -
|
||||
> there are other ways to contribute than code that we'd request you to
|
||||
> consider.
|
||||
Code that changes the behaviour of the product might not get merged, at least not initially. The PR can serve as a discussion bed, but you might find it easier to just start a discussion instead, or post your perspective in the (likely) existing thread about the behaviour change or new feature you wish for.
|
||||
|
||||
## Leave a review or star
|
||||
|
||||
|
||||
@@ -71,6 +71,9 @@
|
||||
],
|
||||
"hex": "fd4b2d"
|
||||
},
|
||||
{
|
||||
"title": "availity"
|
||||
},
|
||||
{
|
||||
"title": "AzurHosts",
|
||||
"slug": "azurhosts",
|
||||
@@ -121,6 +124,13 @@
|
||||
{
|
||||
"title": "Belo"
|
||||
},
|
||||
{
|
||||
"title": "bestbuy",
|
||||
"altNames": [
|
||||
"Best Buy",
|
||||
"bestbuy.com"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Bethesda",
|
||||
"altNames": [
|
||||
@@ -292,6 +302,25 @@
|
||||
{
|
||||
"title": "CoinDCX"
|
||||
},
|
||||
{
|
||||
"title": "colorado",
|
||||
"altNames": [
|
||||
"Colorado.gov",
|
||||
"Colorado Gov",
|
||||
"Colorado Government",
|
||||
"Colorado Government Portal",
|
||||
"Colorado COVES Death Certificates",
|
||||
"Colorado COVES",
|
||||
"Colorado Official State Web Portal",
|
||||
"Colorado State Web Portal",
|
||||
"Colorado State Portal",
|
||||
"Colorado Web Portal",
|
||||
"Colorado Portal",
|
||||
"Colorado State",
|
||||
"Colorado PEAK",
|
||||
"myColorado"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "ConfigCat"
|
||||
},
|
||||
@@ -403,6 +432,13 @@
|
||||
"Murena"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "emeritihealth",
|
||||
"altNames": [
|
||||
"Emeriti Health",
|
||||
"Emeriti Retirement Health",
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "eneba"
|
||||
},
|
||||
@@ -706,6 +742,14 @@
|
||||
{
|
||||
"title": "Letterboxd"
|
||||
},
|
||||
{
|
||||
"title": "lincolnfinancial",
|
||||
"altNames": [
|
||||
"Lincoln Financial",
|
||||
"Lincoln Financial Group",
|
||||
"LFG"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "LinkedIn",
|
||||
"slug": "linkedin"
|
||||
@@ -739,6 +783,10 @@
|
||||
"lu.ma"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "MangaDex",
|
||||
"slug": "mangadex"
|
||||
},
|
||||
{
|
||||
"title": "Marketplace.tf",
|
||||
"slug": "marketplacedottf"
|
||||
@@ -838,6 +886,9 @@
|
||||
"title": "Name.com",
|
||||
"slug": "name_com"
|
||||
},
|
||||
{
|
||||
"title": "nasdaq"
|
||||
},
|
||||
{
|
||||
"title": "Nextcloud",
|
||||
"slug": "nextcloud"
|
||||
@@ -922,6 +973,9 @@
|
||||
{
|
||||
"title": "NuCommunity"
|
||||
},
|
||||
{
|
||||
"title": "numerai"
|
||||
},
|
||||
{
|
||||
"title": "NVIDIA"
|
||||
},
|
||||
@@ -952,6 +1006,10 @@
|
||||
"title": "Oracle Cloud",
|
||||
"slug": "oracle_cloud"
|
||||
},
|
||||
{
|
||||
"title": "Pangolin",
|
||||
"slug": "pangolin"
|
||||
},
|
||||
{
|
||||
"title": "Parqet",
|
||||
"slug": "parqet"
|
||||
@@ -1237,6 +1295,10 @@
|
||||
"PAYDAY 3"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Startmail",
|
||||
"slug": "startmail"
|
||||
},
|
||||
{
|
||||
"title": "STRATO",
|
||||
"hex": "FF8800"
|
||||
@@ -1370,6 +1432,16 @@
|
||||
"title": "Ubuntu One",
|
||||
"slug": "ubuntu_one"
|
||||
},
|
||||
{
|
||||
"title": "unitedhealthgroup",
|
||||
"altNames": [
|
||||
"Unitedhealth Group",
|
||||
"United Healthgroup",
|
||||
"UHG",
|
||||
"uhg.com",
|
||||
"unitedhealthgroup.com"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Unity",
|
||||
"hex": "858585"
|
||||
|
||||
10
mobile/apps/auth/assets/custom-icons/icons/availity.svg
Normal file
|
After Width: | Height: | Size: 15 KiB |
1
mobile/apps/auth/assets/custom-icons/icons/bestbuy.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 638 638"><defs><style>.cls-1{fill:#104ab8;}.cls-1,.cls-2,.cls-3,.cls-4{fill-rule:evenodd;}.cls-2{fill:#fff;}.cls-3{fill:#ffed31;}.cls-4{fill:#1c252c;}</style></defs><title>Best Buy Logo Vector</title><g id="logo-en"><polygon class="cls-1" points="0 0 638 0 638 638 0 638 0 0 0 0"/><path class="cls-2" d="M232,347.26V482.49h70.8c27.87,0,53.62-9.78,53.62-38.51,0-19.42-14-28.29-29.38-32.93,9.37-3.76,20-11.66,20-28.23,0-21.2-21-35.56-49.35-35.56Zm43.37,31.5h15.95c6.49,0,11.44,5.08,11.44,10.24,0,4.8-5.14,9.93-11.44,9.93H275.41V378.76Zm0,48.23h21c7.35,0,13.52,5.59,13.52,12,0,6.78-5.77,12.4-14.79,12.4H275.41V427Z" transform="translate(-181 -181)"/><path class="cls-2" d="M267.12,497.24V632.46h70.8c27.87,0,53.62-9.78,53.62-38.51,0-19.42-14-28.29-29.38-32.93,9.38-3.76,20-11.66,20-28.22,0-21.2-21-35.56-49.35-35.56Zm43.37,31.49h15.95c6.49,0,11.44,5.08,11.44,10.25,0,4.79-5.14,9.92-11.44,9.92H310.49V528.73Zm0,48.24h21.05c7.35,0,13.52,5.58,13.52,12,0,6.77-5.77,12.4-14.79,12.4H310.49V577Z" transform="translate(-181 -181)"/><polygon class="cls-2" points="178.87 301.47 178.87 166.25 287.94 166.25 287.94 198.9 222.11 198.9 222.11 216.64 275.56 216.64 275.56 247.25 222.11 247.25 222.11 268.94 287.94 268.94 287.94 301.47 178.87 301.47 178.87 301.47"/><path class="cls-2" d="M528,486.34c30.7,0,55.26-17.51,55.26-45.89,0-45.8-59.58-38.75-59.58-53,0-5.51,5.79-8.43,11.73-8.43,10.24,0,17.64,6.74,17.64,6.74l25.63-24.41c-10.25-9.75-26.44-18-48.26-18-32.75,0-54.19,19.45-54.19,42.9,0,46.38,58.73,39.65,58.73,54.2,0,5.11-4.91,10.24-13.81,10.24-10.11,0-18.12-6.1-24.35-11.28L471,464c10.38,10.12,27,22.32,57,22.32Z" transform="translate(-181 -181)"/><polygon class="cls-2" points="435.66 301.47 435.66 198.78 399.94 198.78 399.94 166.25 514.75 166.25 514.75 198.78 479.04 198.78 479.04 301.47 435.66 301.47 435.66 301.47"/><path class="cls-2" d="M393,497.14h43.25v81c0,8.22,8.19,15.8,16.72,15.8,8.05,0,16.19-6.84,16.19-16.23V497.14h43.12v80c0,31.76-26.36,57.09-60.45,57.09-34.28,0-58.83-27.57-58.83-58.82V497.14Z" transform="translate(-181 -181)"/><polygon class="cls-2" points="384.63 451.37 384.63 403.05 333.81 316.14 377.56 316.14 406.32 362.7 435.18 316.14 479.04 316.14 428.01 403.54 428.01 451.37 384.63 451.37 384.63 451.37"/><polygon class="cls-3" points="479.04 390.15 453.52 415.66 453.52 450.11 479.04 475.62 586.96 475.62 586.96 390.15 479.04 390.15 479.04 390.15"/><path class="cls-4" d="M655.85,613.87a5.75,5.75,0,1,1-5.75-5.74,5.74,5.74,0,0,1,5.75,5.74Z" transform="translate(-181 -181)"/></g></svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
16
mobile/apps/auth/assets/custom-icons/icons/colorado.svg
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="254" height="197">
|
||||
<path d="M0 0 C83.82 0 167.64 0 254 0 C254 65.01 254 130.02 254 197 C170.18 197 86.36 197 0 197 C0 131.99 0 66.98 0 0 Z " fill="#255E39" transform="translate(0,0)"/>
|
||||
<path d="M0 0 C83.82 0 167.64 0 254 0 C254 20.13 254 40.26 254 61 C252 58 252 58 250.44140625 55.1015625 C245.56357778 46.07605867 239.90237971 38.57068879 233 31 C232.40574219 30.32324219 231.81148437 29.64648438 231.19921875 28.94921875 C220.24437377 17.12451014 203.62450839 8.5338504 188 5 C186.79472656 4.72285156 185.58945313 4.44570312 184.34765625 4.16015625 C158.77417322 -1.25753788 132.93293113 2.2138374 110.6875 16.3125 C104.6016815 20.31637252 99.20467262 24.91572017 94 30 C92.94425781 31.01513672 92.94425781 31.01513672 91.8671875 32.05078125 C88.51106627 35.20766805 88.51106627 35.20766805 86 39 C81.91471475 37.63823825 81.46098573 35.78559446 79.3828125 32.05859375 C79.04377899 31.45749283 78.70474548 30.85639191 78.35543823 30.23707581 C77.26990008 28.30862811 76.19708095 26.37345578 75.125 24.4375 C73.71411291 21.91357977 72.29846937 19.39234086 70.8828125 16.87109375 C70.54493713 16.26711761 70.20706177 15.66314148 69.85894775 15.04086304 C67.36815994 10.6040756 64.75182831 6.27882324 62 2 C59.33448576 4.40817141 57.69828306 6.99238148 55.91796875 10.1015625 C55.32242188 11.13708252 54.726875 12.17260254 54.11328125 13.23950195 C53.47777344 14.35671631 52.84226563 15.47393066 52.1875 16.625 C51.52109934 17.78945891 50.85442872 18.95376337 50.1875 20.11791992 C40.93068749 36.30218373 31.75265124 52.53667981 23 69 C27.95 68.01 32.9 67.02 38 66 C35.92604704 72.67301098 33.33060795 78.79820286 30.25 85.0625 C29.27859712 87.06444928 28.30863363 89.06709742 27.33984375 91.0703125 C26.54876221 92.70468262 26.54876221 92.70468262 25.74169922 94.37207031 C22.3730026 101.36581172 19.10119743 108.40590787 15.8125 115.4375 C15.13382358 116.88746733 14.45511339 118.33741885 13.77636719 119.78735352 C12.18329197 123.1911238 10.59131013 126.59540409 9 130 C12.34653694 130 15.42039041 128.66450025 18.58984375 127.65625 C21 127 21 127 24 127 C22.01427175 133.18398243 19.7878242 139.22422353 17.34765625 145.2421875 C17.00625107 146.09120636 16.66484589 146.94022522 16.31309509 147.81497192 C15.23207037 150.50266587 14.14761226 153.188954 13.0625 155.875 C6.77912222 171.34098251 6.77912222 171.34098251 1 187 C2.11632812 186.61328125 3.23265625 186.2265625 4.3828125 185.828125 C33.68892175 175.93751189 66.60784235 172.70374295 97 180 C97.66676758 180.1551709 98.33353516 180.3103418 99.02050781 180.47021484 C108.69943089 182.7670275 118.15151684 185.81999107 127.46875 189.29296875 C128.6546875 189.73253906 129.840625 190.17210937 131.0625 190.625 C132.12597656 191.02976563 133.18945312 191.43453125 134.28515625 191.8515625 C143.57746226 194.72426278 152.62428287 195.36040388 162.3125 195.3125 C164.10361206 195.30404053 164.10361206 195.30404053 165.9309082 195.29541016 C174.3610904 195.1791794 181.93838254 194.62771049 190 192 C190.93513428 191.70198486 190.93513428 191.70198486 191.88916016 191.39794922 C217.24006336 183.15751684 235.08132548 168.03878987 248.72729492 145.21801758 C251.80476766 140.09761617 251.80476766 140.09761617 254 139 C254 158.14 254 177.28 254 197 C170.18 197 86.36 197 0 197 C0 131.99 0 66.98 0 0 Z " fill="#FDFDFD" transform="translate(0,0)"/>
|
||||
<path d="M0 0 C0.65613281 0.47308594 1.31226563 0.94617187 1.98828125 1.43359375 C8.19172898 6.28906392 12.5611382 12.98165036 16 20 C24.23839404 17.77769262 32.0090674 14.65817187 39.875 11.375 C42.26721473 10.38036914 44.66040281 9.38807517 47.0546875 8.3984375 C48.10124512 7.96192871 49.14780273 7.52541992 50.22607422 7.07568359 C53 6 53 6 57 5 C57 30.41 57 55.82 57 82 C56.01 82.495 56.01 82.495 55 83 C55 81.35 55 79.7 55 78 C51.21452654 76.50479006 47.4282773 75.01156244 43.64135742 73.52001953 C42.35827504 73.01435393 41.0753909 72.50818496 39.79272461 72.00146484 C31.90172043 68.88456448 23.99310874 65.84575736 16 63 C15.525625 64.093125 15.05125 65.18625 14.5625 66.3125 C8.10206161 78.96512328 -2.35460415 87.70575421 -15.4375 93 C-29.87838266 97.0535811 -45.55564041 96.50754481 -58.859375 89.4921875 C-73.15897526 80.99933099 -81.70929125 69.15197361 -86.08203125 53.2265625 C-88.73289686 42.16808167 -87.6277083 31.38695655 -83 21 C-82.39865234 19.61554688 -82.39865234 19.61554688 -81.78515625 18.203125 C-75.22740726 4.7454836 -63.31358344 -4.70987829 -49.4375 -9.9375 C-32.12753124 -14.39739533 -14.1778871 -10.52355405 0 0 Z " fill="#FBFAFA" transform="translate(197,57)"/>
|
||||
<path d="M0 0 C4.17781518 3.50676714 8.11713034 7.1703933 12 11 C12.80566406 11.79277344 13.61132812 12.58554687 14.44140625 13.40234375 C22.19006532 21.50137697 28.59401999 31.29414841 32 42 C31.29689697 42.29277832 30.59379395 42.58555664 29.86938477 42.88720703 C28.88252686 43.29825684 27.89566895 43.70930664 26.87890625 44.1328125 C25.7820752 44.58946289 24.68524414 45.04611328 23.55517578 45.51660156 C21.21112215 46.49465882 18.86831579 47.47571148 16.52685547 48.45996094 C13.00083028 49.94082264 9.46957562 51.40863582 5.9375 52.875 C4.72900391 53.38546875 3.52050781 53.8959375 2.27539062 54.421875 C1.12103516 54.89882812 -0.03332031 55.37578125 -1.22265625 55.8671875 C-2.26172119 56.30144043 -3.30078613 56.73569336 -4.37133789 57.18310547 C-7 58 -7 58 -10 57 C-11.4296875 54.92578125 -11.4296875 54.92578125 -12.875 52.3125 C-19.42601148 40.51709955 -29.84217649 32.8446492 -42.5625 28.65625 C-56.42990775 24.92271714 -71.13091577 25.7886915 -83.85546875 32.5078125 C-97.34889321 40.51080907 -106.24401068 52.0414341 -110.5546875 67.203125 C-111.02309404 70.14504676 -111.01460289 72.9684764 -110.99194336 75.94140625 C-111.02545456 84.76399115 -111.02545456 84.76399115 -114.08203125 88.640625 C-116.9592845 90.92925563 -119.95266091 92.94464306 -123 95 C-125.07005107 96.76090923 -127.03276148 98.62570442 -129 100.5 C-143.70570026 114.18796331 -143.70570026 114.18796331 -150 117 C-159.57148912 93.63001896 -160.76344158 66.4060235 -151.12304688 42.85791016 C-141.87944936 21.5593662 -127.22320872 4.39468338 -107 -7 C-106.02675781 -7.56203125 -105.05351563 -8.1240625 -104.05078125 -8.703125 C-71.86898073 -26.08920004 -28.99166492 -22.13908958 0 0 Z " fill="#C30230" transform="translate(222,20)"/>
|
||||
<path d="M0 0 C4.45538794 1.64894619 8.90185574 3.3200767 13.34472656 5.00244141 C15.22690683 5.70955694 17.1119842 6.40898279 18.99902344 7.10302734 C22.15076109 8.26257362 25.29431886 9.44248243 28.4375 10.625 C29.46238525 10.99681396 30.48727051 11.36862793 31.54321289 11.75170898 C32.97395142 12.29613647 32.97395142 12.29613647 34.43359375 12.8515625 C35.71834595 13.32843506 35.71834595 13.32843506 37.02905273 13.81494141 C39 15 39 15 39.84375 16.9699707 C40.49342913 25.41072381 30.94776473 35.75864029 26 42 C25.40703125 42.75925781 24.8140625 43.51851562 24.203125 44.30078125 C9.68750758 61.76294494 -14.28234764 74.8665569 -37 77 C-61.22105191 78.03276946 -85.35950409 76.70549321 -105 61 C-105.74378906 60.44441406 -106.48757812 59.88882813 -107.25390625 59.31640625 C-123.79829816 46.85775982 -123.79829816 46.85775982 -126 40 C-125.10539062 39.23816406 -124.21078125 38.47632813 -123.2890625 37.69140625 C-116.87637214 32.21935059 -110.50956553 26.71190714 -104.265625 21.046875 C-100.88076288 17.98882715 -97.44363278 14.99153574 -94 12 C-90.74459922 13.40639025 -88.85406513 15.05982643 -86.5 17.6875 C-77.43916873 26.95043159 -66.1810049 31.99274637 -53.34033203 32.2980957 C-46.39490631 32.33973156 -39.63170864 32.26258295 -33 30 C-32.21882813 29.75507813 -31.43765625 29.51015625 -30.6328125 29.2578125 C-17.23315198 24.54079805 -7.75035638 15.14019465 -1.25 2.6328125 C-0.8375 1.76398438 -0.425 0.89515625 0 0 Z " fill="#021A71" transform="translate(213,119)"/>
|
||||
<path d="M0 0 C5.35552828 3.39157167 9.73048255 7.37353935 14.31640625 11.7578125 C16.86387172 13.88626254 18.8665855 14.9914145 22 16 C22.95261719 15.09636719 23.90523437 14.19273438 24.88671875 13.26171875 C26.15342443 12.07007711 27.42036387 10.87868391 28.6875 9.6875 C29.31333984 9.09259766 29.93917969 8.49769531 30.58398438 7.88476562 C33.87475275 4.80074709 36.98137595 2.12841083 41 0 C41.66644531 0.61746094 42.33289063 1.23492187 43.01953125 1.87109375 C48.7281423 7.11662866 54.5269502 12.15258764 60.58984375 16.9921875 C66.81970816 22.18205834 66.81970816 22.18205834 67.53125 25.41015625 C67.38452482 37.86345569 62.48204938 48.68984739 54.2265625 58 C46.31381371 65.47871616 35.90872336 71.06835437 25 72 C24.06671875 72.08121094 23.1334375 72.16242188 22.171875 72.24609375 C8.50219507 72.83653804 -2.2982359 67.43653936 -13.1875 59.75 C-21.40487698 51.71523139 -27.73929647 40.75799942 -28.75 29.125 C-28.43874508 24.7355076 -26.48070743 22.6315565 -23.25 19.8046875 C-22.5075 19.20914063 -21.765 18.61359375 -21 18 C-20.154375 17.30132812 -19.30875 16.60265625 -18.4375 15.8828125 C-16.58822798 14.36451814 -14.73128441 12.85552457 -12.8671875 11.35546875 C-8.44244171 7.72042555 -4.21848422 3.87075667 0 0 Z " fill="#031A71" transform="translate(144,75)"/>
|
||||
<path d="M0 0 C0.89460938 0.43441406 1.78921875 0.86882813 2.7109375 1.31640625 C13.56733148 7.178859 20.58301944 16.62066024 25 28 C26.34950368 32.74608059 27 37.065874 27 42 C23.55220914 40.49993609 21.17639467 38.49590738 18.4375 35.9375 C12.27265981 30.28487205 5.96967741 24.64645161 -1 20 C-1.6290625 20.56847656 -2.258125 21.13695313 -2.90625 21.72265625 C-8.23211968 26.5271682 -13.58511587 31.29600848 -19 36 C-23.98743064 34.50843196 -26.93142754 31.54332502 -30.5625 28 C-35.25906082 23.26431569 -35.25906082 23.26431569 -41 20 C-41.66644531 20.61746094 -42.33289063 21.23492187 -43.01953125 21.87109375 C-49.49783882 27.82388826 -56.14900967 33.47570939 -63 39 C-65.00712736 40.65839866 -67.00426503 42.32789773 -69 44 C-69.89444705 31.15474652 -65.74873183 21.17814981 -58 11 C-42.95796817 -5.60359869 -19.74821462 -9.8220012 0 0 Z " fill="#FDD204" transform="translate(185,56)"/>
|
||||
<path d="M0 0 C4.28792054 2.72349458 7.96086865 5.64265272 11.6875 9.125 C14.40593357 11.65919672 16.90611339 13.93740893 20 16 C18.45427224 19.81628832 16.11228755 21.97025743 13.0625 24.6875 C12.57450928 25.12392822 12.08651855 25.56035645 11.58374023 26.01000977 C7.44438884 29.68168288 3.1542964 33.15674428 -1.15625 36.625 C-4.88158413 39.73626806 -8.42845194 43.02239671 -11.94116211 46.37084961 C-14.47323772 48.75074994 -16.77334734 50.66509642 -20 52 C-20.96345444 50.12762629 -21.92053102 48.2519701 -22.875 46.375 C-23.40867187 45.33085938 -23.94234375 44.28671875 -24.4921875 43.2109375 C-27.26231047 37.31186734 -29.44678461 31.53349556 -28 25 C-26.12378629 22.11112789 -23.67216304 20.14329744 -21 18 C-20.154375 17.30132812 -19.30875 16.60265625 -18.4375 15.8828125 C-16.58822798 14.36451814 -14.73128441 12.85552457 -12.8671875 11.35546875 C-8.44244171 7.72042555 -4.21848422 3.87075667 0 0 Z " fill="#7A853B" transform="translate(144,75)"/>
|
||||
<path d="M0 0 C0.30292969 1.05058594 0.60585937 2.10117188 0.91796875 3.18359375 C3.21554955 10.95006406 5.53141385 18.15450633 10 25 C9.34515625 25.52207031 8.6903125 26.04414063 8.015625 26.58203125 C2.85533541 30.74379023 -2.14926847 34.98462376 -7 39.5 C-12.46969905 44.59155144 -18.15506859 49.34016295 -24 54 C-27.98232778 50.33096724 -30.64316353 46.66489768 -33.25 41.9375 C-33.600625 41.32583984 -33.95125 40.71417969 -34.3125 40.08398438 C-35.98121521 37.11559674 -37.4271484 34.37813958 -38 31 C-31.44231174 24.87720206 -24.8192745 18.95918641 -17.8125 13.34765625 C-14.05017212 10.20715756 -10.48696844 6.86610283 -6.97070312 3.453125 C-3.32463848 0 -3.32463848 0 0 0 Z " fill="#6D3C5E" transform="translate(111,106)"/>
|
||||
<path d="M0 0 C5.83043424 4.10970437 11.09789229 8.68615219 16.30078125 13.5625 C18.89058893 15.98553895 18.89058893 15.98553895 21.48046875 17.8125 C24.59352223 20.10458893 26.65980595 21.65401484 27.62890625 25.48828125 C28.05445689 35.19358123 24.02478887 43.92444646 19 52 C18.071875 51.0409375 18.071875 51.0409375 17.125 50.0625 C11.25501774 44.20900645 5.00673282 38.77135771 -1.4375 33.5625 C-7.88257776 28.34767925 -14.13523865 22.86476135 -20 17 C-19.31035156 16.443125 -18.62070313 15.88625 -17.91015625 15.3125 C-11.80242122 10.35258349 -5.83030237 5.28326166 0 0 Z " fill="#35647D" transform="translate(184,75)"/>
|
||||
<path d="M0 0 C0.66 0.33 1.32 0.66 2 1 C-1.63 4.63 -5.26 8.26 -9 12 C-9.66 11.67 -10.32 11.34 -11 11 C-9.92173635 9.72486237 -8.83714481 8.45507431 -7.75 7.1875 C-7.14671875 6.47980469 -6.5434375 5.77210937 -5.921875 5.04296875 C-4.07629602 3.08110329 -2.20054668 1.54330375 0 0 Z " fill="#FEDE22" transform="translate(135,60)"/>
|
||||
<path d="M0 0 C3.75931429 1.03381143 5.16972619 2.16972619 8 5 C4.37 4.01 0.74 3.02 -3 2 C-2.01 1.34 -1.02 0.68 0 0 Z " fill="#45755B" transform="translate(109,181)"/>
|
||||
<path d="M0 0 C0.33 0.66 0.66 1.32 1 2 C6.445 1.505 6.445 1.505 12 1 C12 1.33 12 1.66 12 2 C7.71 2.66 3.42 3.32 -1 4 C-0.67 2.68 -0.34 1.36 0 0 Z " fill="#4F7F60" transform="translate(24,65)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
11
mobile/apps/auth/assets/custom-icons/icons/emeritihealth.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="121" height="111">
|
||||
<path d="M0 0 C39.93 0 79.86 0 121 0 C121 36.63 121 73.26 121 111 C81.07 111 41.14 111 0 111 C0 74.37 0 37.74 0 0 Z " fill="#FDFDFC" transform="translate(0,0)"/>
|
||||
<path d="M0 0 C1.34094727 -0.00483398 1.34094727 -0.00483398 2.70898438 -0.00976562 C15.92329958 0.09656756 29.05768111 5.15920937 38.69921875 14.3671875 C46.33623486 22.91378081 51.45414229 32.03867724 53.4375 43.3125 C55.4175 42.9825 57.3975 42.6525 59.4375 42.3125 C56.40996635 50.06943534 46.82853593 55.97682128 39.5 59.3125 C37.82136046 60.00117263 36.13406518 60.66925254 34.4375 61.3125 C33.5609375 61.67085937 32.684375 62.02921875 31.78125 62.3984375 C20.17960201 66.92308022 6.59150797 64.44771949 -4.48828125 59.66015625 C-6.56191878 58.74121933 -8.62982096 57.81032187 -10.69677734 56.87646484 C-21.71786864 51.93615813 -31.96757967 49.00024219 -43.6875 53.25 C-49.85297476 56.02724089 -53.58090596 59.9493408 -57.5625 65.3125 C-58.41134766 66.43978516 -58.41134766 66.43978516 -59.27734375 67.58984375 C-59.70144531 68.15832031 -60.12554687 68.72679688 -60.5625 69.3125 C-60.21265815 63.71503042 -56.42242893 59.17242893 -52.5625 55.3125 C-52.45417831 53.23031638 -52.37676459 51.14650871 -52.3125 49.0625 C-51.32728272 34.37380601 -44.38377625 22.27099897 -33.859375 12.1953125 C-23.67156706 3.84408218 -13.05318857 -0.23629958 0 0 Z " fill="#7D9C07" transform="translate(60.5625,2.6875)"/>
|
||||
<path d="M0 0 C39.93 0 79.86 0 121 0 C121 15.18 121 30.36 121 46 C118.69 46 116.38 46 114 46 C113.72027344 45.10539062 113.44054687 44.21078125 113.15234375 43.2890625 C110.46208014 35.17501984 107.34684612 28.77440212 102 22 C101.401875 21.113125 100.80375 20.22625 100.1875 19.3125 C91.26978576 9.88520209 79.12630771 4.33913818 66.27734375 3.7265625 C48.74009638 3.25612695 36.53738751 7.13555151 23.3125 19.1875 C14.84180419 28.0987521 10.10977052 38.99431955 9.6875 51.125 C9.34944073 57.79987234 7.81473954 60.61643116 3 65 C2.01 66.32 1.02 67.64 0 69 C0 46.23 0 23.46 0 0 Z " fill="#FDFEFC" transform="translate(0,0)"/>
|
||||
<path d="M0 0 C7.7514671 5.16764473 13.82626339 13.90836132 16.703125 22.71875 C16.703125 24.03875 16.703125 25.35875 16.703125 26.71875 C11.46215703 26.16706916 7.64974874 24.39540175 3.078125 21.96875 C-14.90773287 12.27142089 -14.90773287 12.27142089 -34.734375 12.4375 C-39.88111467 14.59356662 -45.70693353 17.53886707 -48.296875 22.71875 C-47.08 22.615625 -45.863125 22.5125 -44.609375 22.40625 C-34.14474874 22.43895196 -24.29281839 28.5635433 -15.1953125 33.265625 C-10.48229066 35.6284877 -6.28828923 36.00534278 -1.109375 36.28125 C0.01500977 36.34602539 0.01500977 36.34602539 1.16210938 36.41210938 C3.00890875 36.51797685 4.85599334 36.6188476 6.703125 36.71875 C3.65258646 39.76928854 0.59053342 39.47031177 -3.60205078 39.58984375 C-11.64954951 39.3417513 -18.79427187 36.1503666 -26.171875 33.21875 C-39.2911509 28.00909237 -50.24239432 25.58953741 -64.296875 28.71875 C-63.91593636 18.62387614 -58.87170481 10.32516481 -51.57421875 3.50390625 C-35.74413322 -8.9049258 -17.299832 -9.65024642 0 0 Z " fill="#FBFCF9" transform="translate(85.296875,20.28125)"/>
|
||||
<path d="M0 0 C0.07476562 0.59425781 0.14953125 1.18851563 0.2265625 1.80078125 C1.89490592 12.31788733 7.03959282 21.85443765 15 29 C18.20627298 31.27541953 21.4909817 33.22982559 25 35 C26.60294922 35.81017578 26.60294922 35.81017578 28.23828125 36.63671875 C32.50890201 38.42008659 36.60502154 38.5364421 41.1875 38.5 C42.02394043 38.49427979 42.86038086 38.48855957 43.72216797 38.48266602 C54.81538411 38.16495288 64.10602396 33.8155071 72 26 C76.47244994 20.53700746 79.32082187 14.4918547 82 8 C85.96 8 89.92 8 94 8 C91.39683113 21.53647814 84.16445962 32.6524866 73.296875 41.1015625 C60.88291389 49.43665068 45.9146679 53.47483724 31.00488281 50.61767578 C22.25796227 48.44289499 14.98853845 44.65676037 8 39 C6.948125 38.236875 5.89625 37.47375 4.8125 36.6875 C-1.36142088 30.78797561 -5.25051243 24.03047722 -8.4375 16.1875 C-8.69660156 15.55344238 -8.95570312 14.91938477 -9.22265625 14.26611328 C-10.58441341 10.72610583 -11.29621798 7.7930351 -11 4 C-4.5 0 -4.5 0 0 0 Z " fill="#7D9C08" transform="translate(20,59)"/>
|
||||
<path d="M0 0 C2.01050607 0.26263368 4.00961749 0.61336248 6 1 C8.78756093 1.25820896 11.57995448 1.40834288 14.375 1.5625 C15.11105469 1.60568359 15.84710938 1.64886719 16.60546875 1.69335938 C18.40348068 1.79843799 20.20171958 1.89962093 22 2 C19.47636907 4.52363093 17.87673704 4.2605272 14.4296875 4.2734375 C13.71039063 4.26570313 12.99109375 4.25796875 12.25 4.25 C11.54617187 4.25773437 10.84234375 4.26546875 10.1171875 4.2734375 C5.70222524 4.257025 2.15942781 3.64807517 -2 2 C-1.34 1.34 -0.68 0.68 0 0 Z " fill="#DFE7C3" transform="translate(70,55)"/>
|
||||
<path d="M0 0 C0.99 0.33 1.98 0.66 3 1 C2.60167969 1.45890625 2.20335937 1.9178125 1.79296875 2.390625 C-0.91142062 5.52271585 -3.57991771 8.63877459 -6 12 C-5.54300722 6.97307937 -3.00758252 3.85103739 0 0 Z " fill="#9DB444" transform="translate(6,60)"/>
|
||||
<path d="M0 0 C0 0.66 0 1.32 0 2 C0.66 2.33 1.32 2.66 2 3 C-4.625 2.25 -4.625 2.25 -8 0 C-5.13822943 -1.43088528 -3.06624227 -0.59991697 0 0 Z " fill="#E9EED6" transform="translate(76,56)"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 25 KiB |
1
mobile/apps/auth/assets/custom-icons/icons/mangadex.svg
Normal file
|
After Width: | Height: | Size: 5.0 KiB |
12
mobile/apps/auth/assets/custom-icons/icons/nasdaq.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 70.1 60" style="enable-background:new 0 0 70.1 60;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#0090BA;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M53.3,0L37.7,43c-0.4,1.1-1.4,1.8-2.5,2v0h16.5c1.3,0,2.4-0.8,2.8-2L70.1,0H53.3z M33.9,44.3
|
||||
c1,0,1.8-0.5,2.3-1.2c0.1-0.1,0.2-0.3,0.4-0.7l5.7-15.8l-3.3-9.2c-0.4-1-1.4-1.7-2.6-1.7c-1,0-1.8,0.5-2.3,1.2
|
||||
c-0.1,0.1-0.3,0.4-0.4,0.7l-5.7,15.8l3.4,9.2C31.7,43.6,32.7,44.3,33.9,44.3z M18.4,15h16.7v0c-1.2,0.1-2.3,0.9-2.7,2L16.8,60H0
|
||||
l15.6-43C16,15.9,17.1,15,18.4,15z">
|
||||
</path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 719 B |
1
mobile/apps/auth/assets/custom-icons/icons/numerai.svg
Normal file
|
After Width: | Height: | Size: 10 KiB |
22
mobile/apps/auth/assets/custom-icons/icons/pangolin.svg
Normal file
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="900.82861"
|
||||
height="955.20648"
|
||||
viewBox="0 0 238.34422 252.7317"
|
||||
version="1.1"
|
||||
id="svg420"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs417" />
|
||||
<g
|
||||
id="layer1"
|
||||
transform="translate(-13.119542,-5.9258171)">
|
||||
<path
|
||||
d="m 213.66176,90.072122 c 4.95655,0 8.97383,4.018046 8.97383,8.973827 0,4.956581 -4.01728,8.974621 -8.97383,8.974621 -4.95657,0 -8.97462,-4.01804 -8.97462,-8.974621 0,-4.955781 4.01805,-8.973827 8.97462,-8.973827 z m 35.2316,37.450998 c -0.90048,29.80928 -23.66033,69.21262 -54.51292,79.34466 -36.04206,11.836 -63.40991,-5.92226 -72.08409,-26.74061 -6.75754,-16.21966 -1.65117,-35.62363 10.96266,-43.83669 10.6506,-6.93533 30.48543,-8.76736 47.15454,2.19144 -5.85627,-15.34246 -21.62491,-25.4256 -35.59101,-28.49424 -13.96613,-3.06867 -28.38324,0.43858 -38.74504,5.69946 13.29071,-14.68572 44.40801,-28.946049 78.24077,-10.95958 22.67676,12.05491 32.43775,28.93208 42.0489,51.72763 C 251.59637,117.87858 234.026,71.411066 203.39074,43.794029 172.15544,15.636686 129.95516,4.340214 97.668803,6.103155 108.32483,12.678273 120.84625,22.06586 132.41209,33.053363 81.298533,26.697169 39.174705,38.314245 13.119542,73.749217 27.67508,70.878527 46.868833,69.073666 65.974711,70.016861 28.737658,96.252107 7.1124298,140.38147 18.105298,186.43137 c 6.718497,-11.74129 16.767711,-25.84558 28.726275,-38.62863 -3.677175,34.36994 1.42836,80.83745 45.62293,110.85478 -2.25587,-9.42394 -4.08014,-20.88443 -4.91466,-33.0154 20.673197,16.1282 50.685067,29.42205 87.917917,20.24096 65.77679,-16.21975 83.34719,-79.78335 73.4356,-118.35996"
|
||||
style="fill:#f36118;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.0776283"
|
||||
id="path32" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
15
mobile/apps/auth/assets/custom-icons/icons/startmail.svg
Executable file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 27.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Слой_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="500px" height="500px" viewBox="0 0 500 500" style="enable-background:new 0 0 500 500;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#6573FF;}
|
||||
.st1{fill:#202945;}
|
||||
</style>
|
||||
<g>
|
||||
<path class="st0" d="M500,47.2C500,20.9,478.6,0,452.9,0H47.7C21.4-0.5,0,20.9,0,47.2v43.9c0,0,186.4,180.6,250.5,180.6
|
||||
C319.6,271.7,500,92.2,500,92.2S500,56.5,500,47.2z"/>
|
||||
<path class="st1" d="M0,452.8C0,479.1,21.4,500,47.2,500h405.6c26.3,0,47.2-21.4,47.2-47.2V142.7c0,0-159.2,184.4-249.7,184.4
|
||||
C160.8,327.1,0,178.4,0,178.4C0,236.6,0,395.2,0,452.8z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 843 B |
|
After Width: | Height: | Size: 8.7 KiB |
@@ -232,44 +232,44 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
app_links: 3da4c36b46cac3bf24eb897f1a6ce80bda109874
|
||||
connectivity_plus: 3f6c9057f4cd64198dc826edfb0542892f825343
|
||||
cupertino_http: 94ac07f5ff090b8effa6c5e2c47871d48ab7c86c
|
||||
device_info_plus: 335f3ce08d2e174b9fdc3db3db0f4e3b1f66bd89
|
||||
app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0
|
||||
connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db
|
||||
cupertino_http: 947a233f40cfea55167a49f2facc18434ea117ba
|
||||
device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
|
||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||
file_picker: 9b3292d7c8bc68c8a7bf8eb78f730e49c8efc517
|
||||
file_saver: 6cdbcddd690cb02b0c1a0c225b37cd805c2bf8b6
|
||||
fk_user_agent: 137145b086229251761678fe034da53753f4ce59
|
||||
file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655
|
||||
file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808
|
||||
fk_user_agent: 1f47ec39291e8372b1d692b50084b0d54103c545
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
flutter_email_sender: 2397f5e84aaacfb61af569637a963e7c687858d8
|
||||
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
||||
flutter_local_authentication: 989278c681612f1ee0e36019e149137f114b9d7f
|
||||
flutter_local_notifications: ad39620c743ea4c15127860f4b5641649a988100
|
||||
flutter_native_splash: 35ddbc7228eafcb3969dcc5f1fbbe27c1145a4f0
|
||||
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
|
||||
fluttertoast: 76fea30fcf04176325f6864c87306927bd7d2038
|
||||
local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
|
||||
move_to_background: 155f7bfbd34d43ad847cb630d2d2d87c17199710
|
||||
flutter_email_sender: 10a22605f92809a11ef52b2f412db806c6082d40
|
||||
flutter_inappwebview_ios: 6f63631e2c62a7c350263b13fa5427aedefe81d4
|
||||
flutter_local_authentication: 1172a4dd88f6306dadce067454e2c4caf07977bb
|
||||
flutter_local_notifications: 4cde75091f6327eb8517fa068a0a5950212d2086
|
||||
flutter_native_splash: edf599c81f74d093a4daf8e17bd7a018854bc778
|
||||
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
||||
fluttertoast: e9a18c7be5413da53898f660530c56f35edfba9c
|
||||
local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3
|
||||
move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d
|
||||
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
||||
objective_c: 89e720c30d716b036faf9c9684022048eee1eee2
|
||||
objective_c: 77e887b5ba1827970907e10e832eec1683f3431d
|
||||
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
|
||||
package_info_plus: 580e9a5f1b6ca5594e7c9ed5f92d1dfb2a66b5e1
|
||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||
privacy_screen: 3159a541f5d3a31bea916cfd4e58f9dc722b3fd4
|
||||
qr_code_scanner: d77f94ecc9abf96d9b9b8fc04ef13f611e5a147a
|
||||
package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c
|
||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||
privacy_screen: 1a131c052ceb3c3659934b003b0d397c2381a24e
|
||||
qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e
|
||||
SDWebImage: f84b0feeb08d2d11e6a9b843cb06d75ebf5b8868
|
||||
Sentry: da60d980b197a46db0b35ea12cb8f39af48d8854
|
||||
sentry_flutter: 27892878729f42701297c628eb90e7c6529f3684
|
||||
share_plus: 011d6fb4f9d2576b83179a3a5c5e323202cdabcf
|
||||
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
|
||||
sodium_libs: 6c6d0e83f4ee427c6464caa1f1bdc2abf3ca0b7f
|
||||
sqflite: c35dad70033b8862124f8337cc994a809fcd9fa3
|
||||
sentry_flutter: 2df8b0aab7e4aba81261c230cbea31c82a62dd1b
|
||||
share_plus: 8875f4f2500512ea181eef553c3e27dba5135aad
|
||||
shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78
|
||||
sodium_libs: 1faae17af662384acbd13e41867a0008cd2e2318
|
||||
sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec
|
||||
sqlite3: 0bb0e6389d824e40296f531b858a2a0b71c0d2fb
|
||||
sqlite3_flutter_libs: 9379996d65aa23dcda7585a5b58766cebe0aa042
|
||||
sqlite3_flutter_libs: c00457ebd31e59fa6bb830380ddba24d44fbcd3b
|
||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||
Toast: 1f5ea13423a1e6674c4abdac5be53587ae481c4e
|
||||
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
|
||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||
|
||||
PODFILE CHECKSUM: 78f002751f1a8f65042b8da97902ba4124271c5a
|
||||
|
||||
|
||||
@@ -78,6 +78,7 @@
|
||||
"contactSupport": "Звярнуцца ў службу падтрымкі",
|
||||
"rateUsOnStore": "Ацаніць нас у {storeName}",
|
||||
"blog": "Блог",
|
||||
"merchandise": "Тавары",
|
||||
"verifyPassword": "Праверыць пароль",
|
||||
"pleaseWait": "Пачакайце...",
|
||||
"generatingEncryptionKeysTitle": "Генерацыя ключоў шыфравання...",
|
||||
@@ -85,6 +86,9 @@
|
||||
"useRecoveryKey": "Выкарыстоўваць ключ аднаўлення",
|
||||
"incorrectPasswordTitle": "Няправільны пароль",
|
||||
"welcomeBack": "З вяртаннем!",
|
||||
"emailAlreadyRegistered": "Электронная пошта ўжо зарэгістравана.",
|
||||
"emailNotRegistered": "Электронная пошта не зарэгістравана.",
|
||||
"madeWithLoveAtPrefix": "зроблена з ❤️ у ",
|
||||
"changeEmail": "Змяніць адрас электроннай пошты",
|
||||
"changePassword": "Змяніць пароль",
|
||||
"data": "Даныя",
|
||||
@@ -94,9 +98,13 @@
|
||||
"passwordForDecryptingExport": "Пароль для дэшыфроўкі экспартавання",
|
||||
"passwordEmptyError": "Пароль не можа быць пустым",
|
||||
"importFromApp": "Імпартаваць коды з {appName}",
|
||||
"importSelectJsonFile": "Выбраць файл JSON",
|
||||
"importSelectAppExport": "Выберыце файл экспартавання {appName}",
|
||||
"exportCodes": "Экспартаваць коды",
|
||||
"importLabel": "Імпарт",
|
||||
"selectFile": "Выбраць файл",
|
||||
"emailVerificationToggle": "Праверка эл. пошты",
|
||||
"authenticateGeneric": "Прайдзіце аўтэнтыфікацыю",
|
||||
"ok": "OK",
|
||||
"cancel": "Скасаваць",
|
||||
"yes": "Так",
|
||||
@@ -113,19 +121,29 @@
|
||||
"enterYourPasswordHint": "Увядзіце ваш пароль",
|
||||
"forgotPassword": "Забылі пароль",
|
||||
"oops": "Вой",
|
||||
"suggestFeatures": "Прапанаваць функцыю",
|
||||
"faq": "Частыя пытанні",
|
||||
"leaveFamily": "Пакінуць сямейны план",
|
||||
"inFamilyPlanMessage": "Вы ўдзельнік сямейнага плана!",
|
||||
"scan": "Сканіраваць",
|
||||
"scanACode": "Сканіраваць код",
|
||||
"verify": "Праверыць",
|
||||
"verifyEmail": "Праверыць электронную пошту",
|
||||
"lostDeviceTitle": "Згубілі прыладу?",
|
||||
"twoFactorAuthTitle": "Двухфактарная аўтэнтыфікацыя",
|
||||
"passkeyAuthTitle": "Праверка ключа доступу",
|
||||
"verifyPasskey": "Праверыць ключ доступу",
|
||||
"loginWithTOTP": "Увайсці з TOTP",
|
||||
"recoverAccount": "Аднавіць уліковы запіс",
|
||||
"enterRecoveryKeyHint": "Увядзіце свой ключ аднаўлення",
|
||||
"recover": "Аднавіць",
|
||||
"invalidQRCode": "Памылковы QR-код",
|
||||
"noRecoveryKeyTitle": "Няма ключа аднаўлення?",
|
||||
"enterEmailHint": "Увядзіце свой адрас электроннай пошты",
|
||||
"enterNewEmailHint": "Увядзіце свой новы адрас электроннай пошты",
|
||||
"invalidEmailTitle": "Памылковы адрас электроннай пошты",
|
||||
"deleteAccount": "Выдаліць уліковы запіс",
|
||||
"yesSendFeedbackAction": "Так. Адправіць водгук",
|
||||
"noDeleteAccountAction": "Не, выдаліць уліковы запіс",
|
||||
"sendEmail": "Адправіць ліст",
|
||||
"createNewAccount": "Стварыць новы ўліковы запіс",
|
||||
@@ -140,16 +158,21 @@
|
||||
"social": "Сацыяльныя сеткі",
|
||||
"security": "Бяспека",
|
||||
"lockscreen": "Экран блакіроўкі",
|
||||
"viewActiveSessions": "Паглядзець актыўныя сеансы",
|
||||
"searchHint": "Пошук...",
|
||||
"search": "Пошук",
|
||||
"noResult": "Няма вынікаў",
|
||||
"addCode": "Дадаць код",
|
||||
"scanAQrCode": "Сканіраваць QR-код",
|
||||
"enterDetailsManually": "Увесці падрабязнасці ўручную",
|
||||
"edit": "Рэдагаваць",
|
||||
"share": "Абагуліць",
|
||||
"shareCodes": "Абагуліць коды",
|
||||
"restore": "Аднавіць",
|
||||
"copiedToClipboard": "Скапіявана ў буфер абмену",
|
||||
"copiedNextToClipboard": "Скапіяваць наступны код у буфер абмену",
|
||||
"error": "Памылка",
|
||||
"recoveryKeyCopiedToClipboard": "Ключ аднаўлення скапіяваны ў буфер абмену",
|
||||
"doThisLater": "Зрабіць гэта пазней",
|
||||
"saveKey": "Захаваць ключ",
|
||||
"save": "Захаваць",
|
||||
@@ -164,19 +187,27 @@
|
||||
"changePasswordTitle": "Змяніць пароль",
|
||||
"resetPasswordTitle": "Скінуць пароль",
|
||||
"encryptionKeys": "Ключы шыфравання",
|
||||
"passwordChangedSuccessfully": "Пароль паспяхова зменены",
|
||||
"generatingEncryptionKeys": "Генерацыя ключоў шыфравання...",
|
||||
"continueLabel": "Працягнуць",
|
||||
"insecureDevice": "Небяспечная прылада",
|
||||
"howItWorks": "Як гэта працуе",
|
||||
"logInLabel": "Увайсці",
|
||||
"logout": "Выйсці",
|
||||
"areYouSureYouWantToLogout": "Вы сапраўды хочаце выйсці?",
|
||||
"yesLogout": "Так, выйсці",
|
||||
"exit": "Выхад",
|
||||
"theme": "Тема",
|
||||
"lightTheme": "Светлая",
|
||||
"darkTheme": "Цёмная",
|
||||
"systemTheme": "Сістэманая",
|
||||
"verifyingRecoveryKey": "Праверка ключа аднаўлення...",
|
||||
"recoveryKeyVerified": "Ключ аднаўлення правераны",
|
||||
"recreatePasswordTitle": "Стварыць пароль паўторна",
|
||||
"invalidKey": "Памылковы ключ",
|
||||
"tryAgain": "Паспрабуйце яшчэ раз",
|
||||
"viewRecoveryKey": "Прагледзець ключ аднаўлення",
|
||||
"confirmRecoveryKey": "Пацвердзіце ключ аднаўлення",
|
||||
"confirmYourRecoveryKey": "Пацвердзіце свой ключ аднаўлення",
|
||||
"confirm": "Пацвердзіць",
|
||||
"emailYourLogs": "Адправіць журналы",
|
||||
@@ -221,15 +252,22 @@
|
||||
"pendingSyncs": "Папярэджанне",
|
||||
"pendingSyncsWarningBody": "Некаторыя вашы коды не былі зарэзерваваны.\n\nПераканайцеся, што ў вас ёсць іх рэзервовая копія перад выхадам з сістэмы.",
|
||||
"checkInboxAndSpamFolder": "Праверце свае ўваходныя лісты (і спам) для завяршэння праверкі",
|
||||
"tapToEnterCode": "Націсніце, каб увесці код",
|
||||
"resendEmail": "Адправіць ліст яшчэ раз",
|
||||
"manualSort": "Карыстальніцкая",
|
||||
"editOrder": "Рэдагаваць заказ",
|
||||
"mostFrequentlyUsed": "Часта выкарыстоўваюцца",
|
||||
"mostRecentlyUsed": "Нядаўна выкарыстаныя",
|
||||
"activeSessions": "Актыўныя сеансы",
|
||||
"terminateSession": "Перарваць сеанс?",
|
||||
"terminate": "Перарваць",
|
||||
"thisDevice": "Гэта прылада",
|
||||
"thisEmailIsAlreadyInUse": "Гэта электронная пошта ўжо выкарыстоўваецца",
|
||||
"yourVerificationCodeHasExpired": "Ваш праверачны код пратэрмінаваны",
|
||||
"incorrectCode": "Няправільны код",
|
||||
"emailChangedTo": "Электронная пошта зменена на {newEmail}",
|
||||
"authenticationSuccessful": "Аўтэнтыфікацыя паспяхова пройдзена!",
|
||||
"incorrectRecoveryKey": "Няправільны ключ аднаўлення",
|
||||
"enterPassword": "Увядзіце пароль",
|
||||
"selectExportFormat": "Выберыце фармат экспартавання",
|
||||
"exportDialogDesc": "Зашыфраванае экспартаванне будзе абаронена паролем, які вы выберыце.",
|
||||
@@ -249,10 +287,18 @@
|
||||
"focusOnSearchBar": "Сфакусіравацца на пошуку пры запуску праграмы",
|
||||
"confirmUpdatingkey": "Вы сапраўды хочаце абнавіць сакрэтны ключ?",
|
||||
"minimizeAppOnCopy": "Згортваць праграму пры капіяванні",
|
||||
"editCodeAuthMessage": "Прайдзіце аўтэнтыфікацыю, каб рэдагаваць код",
|
||||
"deleteCodeAuthMessage": "Прайдзіце аўтэнтыфікацыю, каб выдаліць код",
|
||||
"showQRAuthMessage": "Прайдзіце аўтэнтыфікацыю, каб паказаць QR-код",
|
||||
"confirmAccountDeleteTitle": "Пацвердзіце выдаленне ўліковага запісу",
|
||||
"androidBiometricHint": "Праверыць ідэнтыфікацыю",
|
||||
"@androidBiometricHint": {
|
||||
"description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters."
|
||||
},
|
||||
"androidBiometricNotRecognized": "Не распазнана. Паспрабуйце яшчэ раз.",
|
||||
"@androidBiometricNotRecognized": {
|
||||
"description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters."
|
||||
},
|
||||
"androidBiometricSuccess": "Паспяхова",
|
||||
"@androidBiometricSuccess": {
|
||||
"description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters."
|
||||
@@ -261,6 +307,22 @@
|
||||
"@androidCancelButton": {
|
||||
"description": "Message showed on a button that the user can click to leave the current dialog. It is used on Android side. Maximum 30 characters."
|
||||
},
|
||||
"androidSignInTitle": "Патрабуецца аўтэнтыфікацыя",
|
||||
"@androidSignInTitle": {
|
||||
"description": "Message showed as a title in a dialog which indicates the user that they need to scan biometric to continue. It is used on Android side. Maximum 60 characters."
|
||||
},
|
||||
"androidBiometricRequiredTitle": "Патрабуецца біяметрыя",
|
||||
"@androidBiometricRequiredTitle": {
|
||||
"description": "Message showed as a title in a dialog which indicates the user has not set up biometric authentication on their device. It is used on Android side. Maximum 60 characters."
|
||||
},
|
||||
"androidDeviceCredentialsRequiredTitle": "Патрабуюцца ўліковыя даныя прылады",
|
||||
"@androidDeviceCredentialsRequiredTitle": {
|
||||
"description": "Message showed as a title in a dialog which indicates the user has not set up credentials authentication on their device. It is used on Android side. Maximum 60 characters."
|
||||
},
|
||||
"androidDeviceCredentialsSetupDescription": "Патрабуюцца ўліковыя даныя прылады",
|
||||
"@androidDeviceCredentialsSetupDescription": {
|
||||
"description": "Message advising the user to go to the settings and configure device credentials on their device. It shows in a dialog on Android side."
|
||||
},
|
||||
"goToSettings": "Перайсці ў налады",
|
||||
"@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."
|
||||
@@ -269,11 +331,24 @@
|
||||
"@iOSOkButton": {
|
||||
"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": "Адсутнічае падключэнне да інтэрнэту",
|
||||
"signOutFromOtherDevices": "Выйсці з іншых прылад",
|
||||
"signOutOtherDevices": "Выйсці на іншых прыладах",
|
||||
"doNotSignOut": "Не выходзіць",
|
||||
"waitingForBrowserRequest": "Чаканне запыту браўзера...",
|
||||
"waitingForVerification": "Чаканне праверкі...",
|
||||
"passkey": "Ключ доступу",
|
||||
"passKeyPendingVerification": "Праверка пакуль яшчэ не завершана",
|
||||
"loginSessionExpired": "Сеанс завяршыўся",
|
||||
"developerSettings": "Налады распрацоўшчыка",
|
||||
"serverEndpoint": "Канцавы пункт сервера",
|
||||
"invalidEndpoint": "Памылковы канцавы пункт",
|
||||
"endpointUpdatedMessage": "Канцавы пункт паспяхова абноўлены",
|
||||
"customEndpoint": "Падключана да {endpoint}",
|
||||
"pinText": "Замацаваць",
|
||||
"unpinText": "Адмацаваць",
|
||||
"pinnedCodeMessage": "{code} быў замацаваны",
|
||||
"unpinnedCodeMessage": "{code} быў адмацаваны",
|
||||
"pinned": "Замацавана",
|
||||
"tags": "Тэгі",
|
||||
"createNewTag": "Стварыць новы тэг",
|
||||
@@ -281,23 +356,38 @@
|
||||
"create": "Стварыць",
|
||||
"editTag": "Рэдагаванне тэг",
|
||||
"deleteTagTitle": "Выдаліць тэг?",
|
||||
"updateNotAvailable": "Абнаўленне недаступна",
|
||||
"viewRawCodes": "Паглядзець неапрацаваныя коды",
|
||||
"rawCodes": "Неапрацаваныя коды",
|
||||
"rawCodeData": "Неапрацаваныя даныя кода",
|
||||
"appLock": "Блакіроўка праграмы",
|
||||
"noSystemLockFound": "Сістэма блакіроўкі не знойдзена",
|
||||
"autoLock": "Аўтаблакіроўка",
|
||||
"immediately": "Адразу",
|
||||
"reEnterPassword": "Увядзіце пароль паўторна",
|
||||
"reEnterPin": "Увядзіце PIN-код яшчэ раз",
|
||||
"next": "Далей",
|
||||
"tooManyIncorrectAttempts": "Занадта шмат няўдалых спроб",
|
||||
"tapToUnlock": "Націсніце для разблакіроўкі",
|
||||
"setNewPassword": "Задаць новы пароль",
|
||||
"deviceLock": "Блакіроўка прылады",
|
||||
"hideContent": "Схаваць змест",
|
||||
"pinLock": "Блакіроўка PIN'ам",
|
||||
"enterPin": "Увядзіце PIN-код",
|
||||
"setNewPin": "Задаць новы PIN",
|
||||
"appLockNotEnabled": "Блакіроўка праграмы не ўключана",
|
||||
"duplicateCodes": "Дублікаты кадоў",
|
||||
"noDuplicates": "✨ Няма дублікатаў",
|
||||
"deduplicateCodes": "Дубліраваныя кады",
|
||||
"deselectAll": "Зняць выбар з усіх",
|
||||
"selectAll": "Выбраць усе",
|
||||
"deleteDuplicates": "Выдаліць дублікаты",
|
||||
"plainHTML": "Звычайны HTML",
|
||||
"tellUsWhatYouThink": "Раскажыце, што вы думаеце",
|
||||
"dropReviewiOS": "Пакіньце водгук у App Store",
|
||||
"dropReviewAndroid": "Пакіньце водгук у Play Store",
|
||||
"giveUsAStarOnGithub": "Адзначце нас зоркай на Github",
|
||||
"loginWithAuthAccount": "Увайдзіце з дапамогай уліковага запісу Auth",
|
||||
"advanced": "Пашыраныя",
|
||||
"algorithm": "Алгарытм",
|
||||
"type": "Тып",
|
||||
|
||||
@@ -173,6 +173,7 @@
|
||||
"invalidQRCode": "Código QR no válido",
|
||||
"noRecoveryKeyTitle": "¿No tienes la clave de recuperación?",
|
||||
"enterEmailHint": "Introduce tu dirección de correo electrónico",
|
||||
"enterNewEmailHint": "Introduce tu nueva dirección de correo electrónico",
|
||||
"invalidEmailTitle": "Dirección de correo electrónico no válida",
|
||||
"invalidEmailMessage": "Por favor, introduce una dirección de correo electrónico válida.",
|
||||
"deleteAccount": "Eliminar cuenta",
|
||||
@@ -513,5 +514,10 @@
|
||||
"free5GB": "5 GB gratis en <bold-green>ente</bold-green> Fotos",
|
||||
"loginWithAuthAccount": "Inicia sesión con tu cuenta de Auth",
|
||||
"freeStorageOffer": "10% de descuento en <bold-green>ente</bold-green> fotos",
|
||||
"freeStorageOfferDescription": "Usa el cupón \"AUTH\" para obtener un 10% de descuento en el primer año"
|
||||
"freeStorageOfferDescription": "Usa el cupón \"AUTH\" para obtener un 10% de descuento en el primer año",
|
||||
"advanced": "Avanzado",
|
||||
"algorithm": "Algoritmo",
|
||||
"type": "Tipo",
|
||||
"period": "Periodo",
|
||||
"digits": "Dígitos"
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
"setupFirstAccount": "Lisa oma esimene kasutajakonto",
|
||||
"importScanQrCode": "Skanneeri QR-koodi",
|
||||
"qrCode": "QR-kood",
|
||||
"importEnterSetupKey": "Sisesta seadistusvõti",
|
||||
"importAccountPageTitle": "Sisesta kasutajakonto üksikasjad",
|
||||
"secretCanNotBeEmpty": "Saladus ei tohi jääda tühjaks",
|
||||
"bothIssuerAndAccountCanNotBeEmpty": "Nii kasutajakonto kui väljaandja ei tohi tühjaks jääda",
|
||||
@@ -32,6 +33,7 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"codeAccountHint": "Kasutajakonto (sina@domeen.com)",
|
||||
"codeTagHint": "Silt",
|
||||
"accountKeyType": "Võtme tüüp",
|
||||
"sessionExpired": "Sessioon on aegunud",
|
||||
@@ -40,7 +42,12 @@
|
||||
},
|
||||
"pleaseLoginAgain": "Palun logi uuesti sisse",
|
||||
"loggingOut": "Väljalogimine...",
|
||||
"timeBasedKeyType": "Ajapõhine (TOTP)",
|
||||
"counterBasedKeyType": "Loenduripõhine (HOTP)",
|
||||
"saveAction": "Salvesta",
|
||||
"nextTotpTitle": "järgmine",
|
||||
"deleteCodeTitle": "Kas kustutame koodi?",
|
||||
"deleteCodeMessage": "Kas sa oled kindel, et soovid selle koodi kustutada? Seda tegevust ei saa tagasi pöörata.",
|
||||
"trash": "Prügikast",
|
||||
"viewLogsAction": "Vaata logisid",
|
||||
"preparingLogsTitle": "Valmistan logisid ette...",
|
||||
@@ -56,7 +63,18 @@
|
||||
"copyEmailAction": "Kopeeri e-posti aadress",
|
||||
"exportLogsAction": "Ekspordi logid",
|
||||
"reportABug": "Teata veast",
|
||||
"contactSupport": "Võtke ühendust klienditoega",
|
||||
"crashAndErrorReporting": "Teatamine vigadest ja kokkujooksmistest",
|
||||
"reportBug": "Teata veast",
|
||||
"emailUsMessage": "Saada meile e-kiri aadressile {email}",
|
||||
"@emailUsMessage": {
|
||||
"placeholders": {
|
||||
"email": {
|
||||
"type": "String"
|
||||
}
|
||||
}
|
||||
},
|
||||
"contactSupport": "Võta ühendust klienditoega",
|
||||
"rateUsOnStore": "Arvusta meid rakendustepoes: {storeName}",
|
||||
"blog": "Blogi",
|
||||
"verifyPassword": "Korda salasõna",
|
||||
"pleaseWait": "Palun oota...",
|
||||
@@ -70,8 +88,13 @@
|
||||
"changeEmail": "Muuda e-posti aadressi",
|
||||
"changePassword": "Muuda salasõna",
|
||||
"data": "Andmed",
|
||||
"importCodes": "Impordi koode",
|
||||
"importTypePlainText": "Votmindamata tekstina",
|
||||
"importTypeEnteEncrypted": "Ente krüptitud ekspordina",
|
||||
"passwordForDecryptingExport": "Salasõna eksporditud andmete dekrüptimiseks",
|
||||
"passwordEmptyError": "Salasõna väli ei saa olla tühi",
|
||||
"importFromApp": "Impordi koodid rakendusest {appName}",
|
||||
"selectFile": "Vali fail",
|
||||
"ok": "Sobib",
|
||||
"cancel": "Katkesta",
|
||||
"yes": "Jah",
|
||||
|
||||
@@ -506,7 +506,7 @@
|
||||
"selectAll": "Chọn tất cả",
|
||||
"deleteDuplicates": "Xóa trùng lặp",
|
||||
"plainHTML": "HTML thuần",
|
||||
"tellUsWhatYouThink": "Hãy cho chúng tôi biết bạn nghĩ gì",
|
||||
"tellUsWhatYouThink": "Cho biết bạn nghĩ gì",
|
||||
"dropReviewiOS": "Đánh giá ngay trên App Store",
|
||||
"dropReviewAndroid": "Đánh giá ngay trên Play Store",
|
||||
"supportEnte": "Hỗ trợ <bold-green>ente</bold-green>",
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
<releases>
|
||||
<release version="4.4.3" date="2025-06-21" />
|
||||
<release version="4.4.2" date="2025-06-21" />
|
||||
<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" />
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
name: ente_auth
|
||||
description: ente two-factor authenticator
|
||||
version: 4.4.1+441
|
||||
version: 4.4.3+443
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
|
||||
@@ -46,27 +46,25 @@ You can alternatively install the build from PlayStore or F-Droid.
|
||||
|
||||
## 🧑💻 Building from source
|
||||
|
||||
1. Install [Flutter v3.24.3](https://flutter.dev/docs/get-started/install) and [Rust v1.85.1](https://www.rust-lang.org/tools/install).
|
||||
1. [Install Flutter v3.24.3](https://flutter.dev/docs/get-started/install).
|
||||
|
||||
2. Install [Flutter Rust Bridge](https://cjycode.com/flutter_rust_bridge/) with `cargo install flutter_rust_bridge_codegen`
|
||||
2. Pull in all submodules with `git submodule update --init --recursive`
|
||||
|
||||
3. Pull in all submodules with `git submodule update --init --recursive`
|
||||
3. Enable repo git hooks `git config core.hooksPath hooks`
|
||||
|
||||
4. Enable repo git hooks `git config core.hooksPath hooks`
|
||||
|
||||
5. If using Visual Studio Code, add the [Flutter
|
||||
4. If using Visual Studio Code, add the [Flutter
|
||||
Intl](https://marketplace.visualstudio.com/items?itemName=localizely.flutter-intl)
|
||||
extension
|
||||
|
||||
6. On Android:
|
||||
5. On Android:
|
||||
|
||||
- For development, run `flutter run -t lib/main.dart --flavor independent`
|
||||
* For development, run `flutter run -t lib/main.dart --flavor independent`
|
||||
|
||||
- For building APK, [setup your
|
||||
* For building APK, [setup your
|
||||
keystore](https://docs.flutter.dev/deployment/android#create-an-upload-keystore)
|
||||
and run `flutter build apk --release --flavor independent`
|
||||
|
||||
7. For iOS, run `flutter build ios`
|
||||
6. For iOS, run `flutter build ios`
|
||||
|
||||
Some common issues and troubleshooting tips are in [docs/dev](docs/dev.md).
|
||||
|
||||
@@ -90,12 +88,11 @@ issue](https://github.com/ente-io/ente/issues/new?title=Request+for+New+Language
|
||||
to have it added.
|
||||
|
||||
## Certificate Fingerprints
|
||||
|
||||
|
||||
- **SHA1**: E1:60:10:18:B6:B0:2E:A3:74:6F:90:67:50:30:29:75:0E:EF:6D:39
|
||||
- **SHA256**: 35:ED:56:81:B7:0B:B3:BD:35:D9:0D:85:6A:F5:69:4C:50:4D:EF:46:AA:D8:3F:77:7B:1C:67:5C:F4:51:35:0B
|
||||
|
||||
To verify these fingerprints, use the following command:
|
||||
|
||||
```bash
|
||||
apksigner verify --print-certs <path_to_apk>
|
||||
```
|
||||
|
||||
@@ -31,7 +31,7 @@ if (keystorePropertiesFile.exists()) {
|
||||
android {
|
||||
namespace = "io.ente.photos"
|
||||
compileSdk = 35
|
||||
ndkVersion = "28.0.13004108"
|
||||
ndkVersion = flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
|
||||
@@ -5,4 +5,5 @@ dart_output: lib/src/rust
|
||||
dart_preamble: |
|
||||
// ignore_for_file: require_trailing_commas
|
||||
|
||||
web: false
|
||||
full_dep: true
|
||||
web: false
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
import "package:photos/src/rust/api/simple.dart";
|
||||
import 'package:photos/src/rust/frb_generated.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
setUpAll(() async => await RustLib.init());
|
||||
testWidgets('Can call rust function', (WidgetTester tester) async {
|
||||
final testString = greet(name: "Tom");
|
||||
expect(testString.contains('Tom'), true);
|
||||
});
|
||||
}
|
||||
@@ -288,7 +288,6 @@ DEPENDENCIES:
|
||||
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
|
||||
- privacy_screen (from `.symlinks/plugins/privacy_screen/ios`)
|
||||
- receive_sharing_intent (from `.symlinks/plugins/receive_sharing_intent/ios`)
|
||||
- rust_lib_photos (from `.symlinks/plugins/rust_lib_photos/ios`)
|
||||
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
|
||||
@@ -416,8 +415,6 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/privacy_screen/ios"
|
||||
receive_sharing_intent:
|
||||
:path: ".symlinks/plugins/receive_sharing_intent/ios"
|
||||
rust_lib_photos:
|
||||
:path: ".symlinks/plugins/rust_lib_photos/ios"
|
||||
sentry_flutter:
|
||||
:path: ".symlinks/plugins/sentry_flutter/ios"
|
||||
share_plus:
|
||||
|
||||
@@ -566,7 +566,6 @@
|
||||
"${BUILT_PRODUCTS_DIR}/photo_manager/photo_manager.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/privacy_screen/privacy_screen.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/receive_sharing_intent/receive_sharing_intent.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/rust_lib_photos/rust_lib_photos.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/sentry_flutter/sentry_flutter.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/share_plus/share_plus.framework",
|
||||
"${BUILT_PRODUCTS_DIR}/shared_preferences_foundation/shared_preferences_foundation.framework",
|
||||
@@ -663,7 +662,6 @@
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/photo_manager.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/privacy_screen.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/receive_sharing_intent.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/rust_lib_photos.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sentry_flutter.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/share_plus.framework",
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_foundation.framework",
|
||||
|
||||
@@ -58,7 +58,7 @@ bool isHandledSyncError(Object errObj) {
|
||||
|
||||
class LockAlreadyAcquiredError extends Error {}
|
||||
|
||||
class LockFreedError extends Error {}
|
||||
class LockFreedError extends Error{}
|
||||
|
||||
class UnauthorizedError extends Error {}
|
||||
|
||||
|
||||
@@ -1,238 +0,0 @@
|
||||
import "dart:typed_data" show Float32List;
|
||||
|
||||
import "package:flutter_rust_bridge/flutter_rust_bridge.dart" show Uint64List;
|
||||
import "package:logging/logging.dart";
|
||||
import "package:path/path.dart";
|
||||
import "package:path_provider/path_provider.dart";
|
||||
import "package:photos/models/ml/vector.dart";
|
||||
import "package:photos/services/machine_learning/semantic_search/query_result.dart";
|
||||
import "package:photos/src/rust/api/usearch_api.dart";
|
||||
|
||||
class ClipVectorDB {
|
||||
static final Logger _logger = Logger("ClipVectorDB");
|
||||
|
||||
static const _databaseName = "ente.ml.vectordb.clip";
|
||||
|
||||
static final BigInt _embeddingDimension = BigInt.from(512);
|
||||
|
||||
static Logger get logger => _logger;
|
||||
|
||||
// Singleton pattern
|
||||
ClipVectorDB._privateConstructor();
|
||||
static final instance = ClipVectorDB._privateConstructor();
|
||||
factory ClipVectorDB() => instance;
|
||||
|
||||
// only have a single app-wide reference to the database
|
||||
static Future<VectorDb>? _vectorDbFuture;
|
||||
|
||||
Future<VectorDb> get _vectorDB async {
|
||||
_vectorDbFuture ??= _initVectorDB();
|
||||
return _vectorDbFuture!;
|
||||
}
|
||||
|
||||
Future<VectorDb> _initVectorDB() async {
|
||||
final documentsDirectory = await getApplicationDocumentsDirectory();
|
||||
final String databaseDirectory =
|
||||
join(documentsDirectory.path, _databaseName);
|
||||
_logger.info("Opening vectorDB access: DB path " + databaseDirectory);
|
||||
final vectorDB = VectorDb(
|
||||
filePath: databaseDirectory,
|
||||
dimensions: _embeddingDimension,
|
||||
);
|
||||
final stats = await getIndexStats(vectorDB);
|
||||
_logger.info("VectorDB connection opened with stats: ${stats.toString()}");
|
||||
|
||||
return vectorDB;
|
||||
}
|
||||
|
||||
Future<void> insertEmbedding({
|
||||
required int fileID,
|
||||
required List<double> embedding,
|
||||
}) async {
|
||||
final db = await _vectorDB;
|
||||
try {
|
||||
await db.addVector(key: BigInt.from(fileID), vector: embedding);
|
||||
} catch (e, s) {
|
||||
_logger.severe("Error inserting embedding", e, s);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> bulkInsertEmbeddings({
|
||||
required List<int> fileIDs,
|
||||
required List<Float32List> embeddings,
|
||||
}) async {
|
||||
final db = await _vectorDB;
|
||||
final bigKeys = Uint64List.fromList(fileIDs);
|
||||
try {
|
||||
await db.bulkAddVectors(keys: bigKeys, vectors: embeddings);
|
||||
} catch (e, s) {
|
||||
_logger.severe("Error bulk inserting embeddings", e, s);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<EmbeddingVector>> getEmbeddings(List<int> fileIDs) async {
|
||||
final db = await _vectorDB;
|
||||
try {
|
||||
final keys = Uint64List.fromList(fileIDs);
|
||||
final vectors = await db.bulkGetVectors(keys: keys);
|
||||
return List.generate(
|
||||
vectors.length,
|
||||
(index) => EmbeddingVector(
|
||||
fileID: fileIDs[index],
|
||||
embedding: vectors[index],
|
||||
),
|
||||
);
|
||||
} catch (e, s) {
|
||||
_logger.severe("Error getting embeddings", e, s);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteEmbeddings(List<int> fileIDs) async {
|
||||
final db = await _vectorDB;
|
||||
try {
|
||||
final deletedCount =
|
||||
await db.bulkRemoveVectors(keys: Uint64List.fromList(fileIDs));
|
||||
_logger.info(
|
||||
"Deleted $deletedCount embeddings, from ${fileIDs.length} keys",
|
||||
);
|
||||
} catch (e, s) {
|
||||
_logger.severe("Error bulk deleting specific embeddings", e, s);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteAllEmbeddings() async {
|
||||
final db = await _vectorDB;
|
||||
try {
|
||||
await db.resetIndex();
|
||||
} catch (e, s) {
|
||||
_logger.severe("Error deleting all embeddings", e, s);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> deleteIndex() async {
|
||||
final db = await _vectorDB;
|
||||
try {
|
||||
await db.deleteIndex();
|
||||
_vectorDbFuture = null;
|
||||
} catch (e, s) {
|
||||
_logger.severe("Error deleting index", e, s);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<VectorDbStats> getIndexStats([VectorDb? db]) async {
|
||||
db ??= await _vectorDB;
|
||||
try {
|
||||
final stats = await db.getIndexStats();
|
||||
return VectorDbStats(
|
||||
size: stats.$1.toInt(),
|
||||
capacity: stats.$2.toInt(),
|
||||
dimensions: stats.$3.toInt(),
|
||||
fileSize: stats.$4.toInt(),
|
||||
memoryUsage: stats.$5.toInt(),
|
||||
expansionAdd: stats.$6.toInt(),
|
||||
expansionSearch: stats.$7.toInt(),
|
||||
);
|
||||
} catch (e, s) {
|
||||
_logger.severe("Error getting index stats", e, s);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<(Uint64List, Float32List)> searchClosestVectors(
|
||||
List<double> query,
|
||||
int count,
|
||||
) async {
|
||||
final db = await _vectorDB;
|
||||
try {
|
||||
final result =
|
||||
await db.searchVectors(query: query, count: BigInt.from(count));
|
||||
return result;
|
||||
} catch (e, s) {
|
||||
_logger.severe("Error searching closest vectors", e, s);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<(BigInt, double)> searchClosestVector(
|
||||
List<double> query,
|
||||
) async {
|
||||
final db = await _vectorDB;
|
||||
try {
|
||||
final result = await db.searchVectors(query: query, count: BigInt.one);
|
||||
return (result.$1[0], result.$2[0]);
|
||||
} catch (e, s) {
|
||||
_logger.severe("Error searching closest vector", e, s);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
Future<Map<String, List<QueryResult>>> computeBulkSimilarities(
|
||||
Map<String, List<double>> textQueryToEmbeddingMap,
|
||||
Map<String, double> minimumSimilarityMap,
|
||||
) async {
|
||||
try {
|
||||
final queryToResults = <String, List<QueryResult>>{};
|
||||
for (final MapEntry<String, List<double>> entry
|
||||
in textQueryToEmbeddingMap.entries) {
|
||||
final query = entry.key;
|
||||
final minimumSimilarity = minimumSimilarityMap[query]!;
|
||||
final textEmbedding = entry.value;
|
||||
final (potentialFileIDs, distances) =
|
||||
await searchClosestVectors(textEmbedding, 1000);
|
||||
final queryResults = <QueryResult>[];
|
||||
for (var i = 0; i < potentialFileIDs.length; i++) {
|
||||
final similarity = 1 - distances[i];
|
||||
if (similarity >= minimumSimilarity) {
|
||||
queryResults
|
||||
.add(QueryResult(potentialFileIDs[i].toInt(), similarity));
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
queryToResults[query] = queryResults;
|
||||
}
|
||||
return queryToResults;
|
||||
} catch (e, s) {
|
||||
_logger.severe(
|
||||
"Could not bulk find embeddings similarities using vector DB",
|
||||
e,
|
||||
s,
|
||||
);
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class VectorDbStats {
|
||||
final int size;
|
||||
final int capacity;
|
||||
final int dimensions;
|
||||
|
||||
// in bytes
|
||||
final int fileSize;
|
||||
final int memoryUsage;
|
||||
|
||||
final int expansionAdd;
|
||||
final int expansionSearch;
|
||||
|
||||
VectorDbStats({
|
||||
required this.size,
|
||||
required this.capacity,
|
||||
required this.dimensions,
|
||||
required this.fileSize,
|
||||
required this.memoryUsage,
|
||||
required this.expansionAdd,
|
||||
required this.expansionSearch,
|
||||
});
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return "VectorDbStats(size: $size, capacity: $capacity, dimensions: $dimensions, file size on disk (bytes): $fileSize, memory usage (bytes): $memoryUsage, expansionAdd: $expansionAdd, expansionSearch: $expansionSearch)";
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
import "dart:io" show File;
|
||||
import "dart:math";
|
||||
|
||||
import "package:collection/collection.dart";
|
||||
@@ -10,7 +9,6 @@ import 'package:path_provider/path_provider.dart';
|
||||
import "package:photos/core/event_bus.dart";
|
||||
import "package:photos/db/common/base.dart";
|
||||
import "package:photos/db/ml/base.dart";
|
||||
import "package:photos/db/ml/clip_vector_db.dart";
|
||||
import "package:photos/db/ml/db_model_mappers.dart";
|
||||
import 'package:photos/db/ml/schema.dart';
|
||||
import "package:photos/events/embedding_updated_event.dart";
|
||||
@@ -20,7 +18,6 @@ import "package:photos/models/ml/face/face.dart";
|
||||
import "package:photos/models/ml/face/face_with_embedding.dart";
|
||||
import "package:photos/models/ml/ml_versions.dart";
|
||||
import "package:photos/models/ml/vector.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/face_clustering/face_db_info_for_clustering.dart";
|
||||
import 'package:photos/services/machine_learning/face_ml/face_filtering/face_filtering_constants.dart';
|
||||
import "package:photos/services/machine_learning/ml_result.dart";
|
||||
@@ -87,8 +84,6 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
|
||||
"MLDataDB Migration took ${stopwatch.elapsedMilliseconds} ms",
|
||||
);
|
||||
stopwatch.stop();
|
||||
_logger.info("Starting CLIP vector DB migration check unawaited");
|
||||
if (flagService.enableVectorDb) unawaited(checkMigrateFillClipVectorDB());
|
||||
|
||||
return asyncDBConnection;
|
||||
}
|
||||
@@ -388,8 +383,12 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
|
||||
}
|
||||
}
|
||||
if (personID == null && clusterID == null) {
|
||||
_logger.severe("personID and clusterID cannot be null both");
|
||||
throw Exception("personID and clusterID cannot be null");
|
||||
}
|
||||
_logger.severe(
|
||||
"Something went wrong finding a face from `getCoverFaceForPerson` (personID: $personID, clusterID: $clusterID)",
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1254,121 +1253,6 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
|
||||
return embeddings;
|
||||
}
|
||||
|
||||
Future<void> checkMigrateFillClipVectorDB({bool force = false}) async {
|
||||
_logger.info("Waiting for ClipVectorDB to be ready");
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
_logger.info("Checking if ClipVectorDB migration is needed");
|
||||
|
||||
// Check if vector DB migration has run
|
||||
_logger.info("Checking if ClipVectorDB migration has run");
|
||||
final documentsDirectory = await getApplicationDocumentsDirectory();
|
||||
final migrationFlagFile =
|
||||
File(join(documentsDirectory.path, 'clip_vector_migration_done'));
|
||||
if (await migrationFlagFile.exists() && !force) {
|
||||
_logger.info("ClipVectorDB migration not needed, already done");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get total count first to track progress
|
||||
_logger.info("Getting total count of clip embeddings");
|
||||
final db = await instance.asyncDB;
|
||||
final countResult =
|
||||
await db.getAll('SELECT COUNT($fileIDColumn) as total FROM $clipTable');
|
||||
final totalCount = countResult.first['total'] as int;
|
||||
if (totalCount == 0) {
|
||||
_logger.info("No clip embeddings to migrate");
|
||||
await migrationFlagFile.create();
|
||||
return;
|
||||
}
|
||||
_logger.info("Total count of clip embeddings: $totalCount");
|
||||
|
||||
_logger.info("First time referencing ClipVectorDB in migration");
|
||||
final clipVectorDB = ClipVectorDB.instance;
|
||||
_logger.info("ClipVectorDB referenced");
|
||||
await clipVectorDB.deleteAllEmbeddings();
|
||||
_logger.info("ClipVectorDB all embeddings cleared");
|
||||
|
||||
_logger
|
||||
.info("Starting migration of $totalCount clip embeddings to vector DB");
|
||||
const batchSize = 1000;
|
||||
int offset = 0;
|
||||
int processedCount = 0;
|
||||
int weirdCount = 0;
|
||||
int whileCount = 0;
|
||||
final stopwatch = Stopwatch()..start();
|
||||
try {
|
||||
while (true) {
|
||||
whileCount++;
|
||||
_logger.info("$whileCount st round of while loop");
|
||||
// Allow some time for any GC to finish
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
|
||||
_logger.info("Reading $batchSize rows from DB");
|
||||
final List<Map<String, dynamic>> results = await db.getAll('''
|
||||
SELECT $fileIDColumn, $embeddingColumn
|
||||
FROM $clipTable
|
||||
ORDER BY $fileIDColumn DESC
|
||||
LIMIT $batchSize OFFSET $offset
|
||||
''');
|
||||
_logger.info("Got ${results.length} results from DB");
|
||||
if (results.isEmpty) {
|
||||
_logger.info("No more results, breaking out of while loop");
|
||||
break;
|
||||
}
|
||||
_logger.info("Processing ${results.length} results");
|
||||
final List<int> fileIDs = [];
|
||||
final List<Float32List> embeddings = [];
|
||||
for (final result in results) {
|
||||
final embedding =
|
||||
Float32List.view((result[embeddingColumn] as Uint8List).buffer);
|
||||
if (embedding.length == 512) {
|
||||
fileIDs.add(result[fileIDColumn] as int);
|
||||
embeddings.add(Float32List.view(result[embeddingColumn].buffer));
|
||||
} else {
|
||||
weirdCount++;
|
||||
}
|
||||
}
|
||||
_logger.info(
|
||||
"Got ${fileIDs.length} valid embeddings, $weirdCount weird embeddings",
|
||||
);
|
||||
|
||||
await ClipVectorDB.instance
|
||||
.bulkInsertEmbeddings(fileIDs: fileIDs, embeddings: embeddings);
|
||||
_logger.info("Inserted ${fileIDs.length} embeddings to ClipVectorDB");
|
||||
processedCount += fileIDs.length;
|
||||
offset += batchSize;
|
||||
_logger.info(
|
||||
"migrated $processedCount/$totalCount embeddings to ClipVectorDB",
|
||||
);
|
||||
if (processedCount >= totalCount) {
|
||||
_logger.info("All embeddings migrated, breaking out of while loop");
|
||||
break;
|
||||
}
|
||||
_logger.info("Clearing out embeddings and fileIDs");
|
||||
embeddings.clear();
|
||||
fileIDs.clear();
|
||||
results.clear();
|
||||
// Allow some time for any GC to finish
|
||||
_logger.info("Waiting for 100ms for GC to finish");
|
||||
await Future.delayed(const Duration(milliseconds: 100));
|
||||
}
|
||||
_logger.info(
|
||||
"migrated all $totalCount embeddings to ClipVectorDB in ${stopwatch.elapsed.inMilliseconds} ms, with $weirdCount weird embeddings not migrated",
|
||||
);
|
||||
await migrationFlagFile.create();
|
||||
_logger.info("ClipVectorDB migration done, flag file created");
|
||||
} catch (e) {
|
||||
_logger.severe(
|
||||
"Error migrating ClipVectorDB after ${stopwatch.elapsed.inMilliseconds} ms, clearing out DB again",
|
||||
e,
|
||||
);
|
||||
await clipVectorDB.deleteAllEmbeddings();
|
||||
rethrow;
|
||||
} finally {
|
||||
stopwatch.stop();
|
||||
}
|
||||
}
|
||||
|
||||
// Get indexed FileIDs
|
||||
@override
|
||||
Future<Map<int, int>> clipIndexedFileWithVersion() async {
|
||||
@@ -1402,25 +1286,12 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
|
||||
'INSERT OR REPLACE INTO $clipTable ($fileIDColumn, $embeddingColumn, $mlVersionColumn) VALUES (?, ?, ?)',
|
||||
_getRowFromEmbedding(embeddings.first),
|
||||
);
|
||||
if (flagService.enableVectorDb) {
|
||||
await ClipVectorDB.instance.insertEmbedding(
|
||||
fileID: embeddings.first.fileID,
|
||||
embedding: embeddings.first.embedding,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
final inputs = embeddings.map((e) => _getRowFromEmbedding(e)).toList();
|
||||
await db.executeBatch(
|
||||
'INSERT OR REPLACE INTO $clipTable ($fileIDColumn, $embeddingColumn, $mlVersionColumn) values(?, ?, ?)',
|
||||
inputs,
|
||||
);
|
||||
if (flagService.enableVectorDb) {
|
||||
await ClipVectorDB.instance.bulkInsertEmbeddings(
|
||||
fileIDs: embeddings.map((e) => e.fileID).toList(),
|
||||
embeddings:
|
||||
embeddings.map((e) => Float32List.fromList(e.embedding)).toList(),
|
||||
);
|
||||
}
|
||||
}
|
||||
Bus.instance.fire(EmbeddingUpdatedEvent());
|
||||
}
|
||||
@@ -1431,9 +1302,6 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
|
||||
await db.execute(
|
||||
'DELETE FROM $clipTable WHERE $fileIDColumn IN (${fileIDs.join(", ")})',
|
||||
);
|
||||
if (flagService.enableVectorDb) {
|
||||
await ClipVectorDB.instance.deleteEmbeddings(fileIDs);
|
||||
}
|
||||
Bus.instance.fire(EmbeddingUpdatedEvent());
|
||||
}
|
||||
|
||||
@@ -1441,9 +1309,6 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
|
||||
Future<void> deleteClipIndexes() async {
|
||||
final db = await instance.asyncDB;
|
||||
await db.execute('DELETE FROM $clipTable');
|
||||
if (flagService.enableVectorDb) {
|
||||
await ClipVectorDB.instance.deleteAllEmbeddings();
|
||||
}
|
||||
Bus.instance.fire(EmbeddingUpdatedEvent());
|
||||
}
|
||||
|
||||
|
||||
@@ -37,32 +37,27 @@ class CenterBox extends $pb.GeneratedMessage {
|
||||
return $result;
|
||||
}
|
||||
CenterBox._() : super();
|
||||
factory CenterBox.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
factory CenterBox.fromJson($core.String i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(i, r);
|
||||
factory CenterBox.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory CenterBox.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'CenterBox',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.common'),
|
||||
createEmptyInstance: create)
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'CenterBox', package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.common'), createEmptyInstance: create)
|
||||
..a<$core.double>(1, _omitFieldNames ? '' : 'x', $pb.PbFieldType.OF)
|
||||
..a<$core.double>(2, _omitFieldNames ? '' : 'y', $pb.PbFieldType.OF)
|
||||
..a<$core.double>(3, _omitFieldNames ? '' : 'height', $pb.PbFieldType.OF)
|
||||
..a<$core.double>(4, _omitFieldNames ? '' : 'width', $pb.PbFieldType.OF)
|
||||
..hasRequiredFields = false;
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
CenterBox clone() => CenterBox()..mergeFromMessage(this);
|
||||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
CenterBox copyWith(void Function(CenterBox) updates) =>
|
||||
super.copyWith((message) => updates(message as CenterBox)) as CenterBox;
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
CenterBox copyWith(void Function(CenterBox) updates) => super.copyWith((message) => updates(message as CenterBox)) as CenterBox;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@@ -71,17 +66,13 @@ class CenterBox extends $pb.GeneratedMessage {
|
||||
CenterBox createEmptyInstance() => create();
|
||||
static $pb.PbList<CenterBox> createRepeated() => $pb.PbList<CenterBox>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static CenterBox getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CenterBox>(create);
|
||||
static CenterBox getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<CenterBox>(create);
|
||||
static CenterBox? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.double get x => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set x($core.double v) {
|
||||
$_setFloat(0, v);
|
||||
}
|
||||
|
||||
set x($core.double v) { $_setFloat(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasX() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
@@ -90,10 +81,7 @@ class CenterBox extends $pb.GeneratedMessage {
|
||||
@$pb.TagNumber(2)
|
||||
$core.double get y => $_getN(1);
|
||||
@$pb.TagNumber(2)
|
||||
set y($core.double v) {
|
||||
$_setFloat(1, v);
|
||||
}
|
||||
|
||||
set y($core.double v) { $_setFloat(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasY() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
@@ -102,10 +90,7 @@ class CenterBox extends $pb.GeneratedMessage {
|
||||
@$pb.TagNumber(3)
|
||||
$core.double get height => $_getN(2);
|
||||
@$pb.TagNumber(3)
|
||||
set height($core.double v) {
|
||||
$_setFloat(2, v);
|
||||
}
|
||||
|
||||
set height($core.double v) { $_setFloat(2, v); }
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasHeight() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
@@ -114,16 +99,13 @@ class CenterBox extends $pb.GeneratedMessage {
|
||||
@$pb.TagNumber(4)
|
||||
$core.double get width => $_getN(3);
|
||||
@$pb.TagNumber(4)
|
||||
set width($core.double v) {
|
||||
$_setFloat(3, v);
|
||||
}
|
||||
|
||||
set width($core.double v) { $_setFloat(3, v); }
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasWidth() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
void clearWidth() => clearField(4);
|
||||
}
|
||||
|
||||
|
||||
const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names');
|
||||
const _omitMessageNames =
|
||||
$core.bool.fromEnvironment('protobuf.omit_message_names');
|
||||
const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names');
|
||||
|
||||
@@ -8,3 +8,4 @@
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
|
||||
@@ -35,3 +35,4 @@ final $typed_data.Uint8List centerBoxDescriptor = $convert.base64Decode(
|
||||
'CglDZW50ZXJCb3gSEQoBeBgBIAEoAkgAUgF4iAEBEhEKAXkYAiABKAJIAVIBeYgBARIbCgZoZW'
|
||||
'lnaHQYAyABKAJIAlIGaGVpZ2h0iAEBEhkKBXdpZHRoGAQgASgCSANSBXdpZHRoiAEBQgQKAl94'
|
||||
'QgQKAl95QgkKB19oZWlnaHRCCAoGX3dpZHRo');
|
||||
|
||||
|
||||
@@ -11,3 +11,4 @@
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
export 'box.pb.dart';
|
||||
|
||||
|
||||
@@ -29,30 +29,25 @@ class EPoint extends $pb.GeneratedMessage {
|
||||
return $result;
|
||||
}
|
||||
EPoint._() : super();
|
||||
factory EPoint.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
factory EPoint.fromJson($core.String i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(i, r);
|
||||
factory EPoint.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory EPoint.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'EPoint',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.common'),
|
||||
createEmptyInstance: create)
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EPoint', package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.common'), createEmptyInstance: create)
|
||||
..a<$core.double>(1, _omitFieldNames ? '' : 'x', $pb.PbFieldType.OF)
|
||||
..a<$core.double>(2, _omitFieldNames ? '' : 'y', $pb.PbFieldType.OF)
|
||||
..hasRequiredFields = false;
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
EPoint clone() => EPoint()..mergeFromMessage(this);
|
||||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
EPoint copyWith(void Function(EPoint) updates) =>
|
||||
super.copyWith((message) => updates(message as EPoint)) as EPoint;
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
EPoint copyWith(void Function(EPoint) updates) => super.copyWith((message) => updates(message as EPoint)) as EPoint;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@@ -61,17 +56,13 @@ class EPoint extends $pb.GeneratedMessage {
|
||||
EPoint createEmptyInstance() => create();
|
||||
static $pb.PbList<EPoint> createRepeated() => $pb.PbList<EPoint>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static EPoint getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<EPoint>(create);
|
||||
static EPoint getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<EPoint>(create);
|
||||
static EPoint? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.double get x => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set x($core.double v) {
|
||||
$_setFloat(0, v);
|
||||
}
|
||||
|
||||
set x($core.double v) { $_setFloat(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasX() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
@@ -80,16 +71,13 @@ class EPoint extends $pb.GeneratedMessage {
|
||||
@$pb.TagNumber(2)
|
||||
$core.double get y => $_getN(1);
|
||||
@$pb.TagNumber(2)
|
||||
set y($core.double v) {
|
||||
$_setFloat(1, v);
|
||||
}
|
||||
|
||||
set y($core.double v) { $_setFloat(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasY() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
void clearY() => clearField(2);
|
||||
}
|
||||
|
||||
|
||||
const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names');
|
||||
const _omitMessageNames =
|
||||
$core.bool.fromEnvironment('protobuf.omit_message_names');
|
||||
const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names');
|
||||
|
||||
@@ -8,3 +8,4 @@
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
|
||||
@@ -30,3 +30,4 @@ const EPoint$json = {
|
||||
final $typed_data.Uint8List ePointDescriptor = $convert.base64Decode(
|
||||
'CgZFUG9pbnQSEQoBeBgBIAEoAkgAUgF4iAEBEhEKAXkYAiABKAJIAVIBeYgBAUIECgJfeEIECg'
|
||||
'JfeQ==');
|
||||
|
||||
|
||||
@@ -11,3 +11,4 @@
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
export 'point.pb.dart';
|
||||
|
||||
|
||||
@@ -26,29 +26,24 @@ class EVector extends $pb.GeneratedMessage {
|
||||
return $result;
|
||||
}
|
||||
EVector._() : super();
|
||||
factory EVector.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
factory EVector.fromJson($core.String i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(i, r);
|
||||
factory EVector.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory EVector.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'EVector',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.common'),
|
||||
createEmptyInstance: create)
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'EVector', package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.common'), createEmptyInstance: create)
|
||||
..p<$core.double>(1, _omitFieldNames ? '' : 'values', $pb.PbFieldType.KD)
|
||||
..hasRequiredFields = false;
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
EVector clone() => EVector()..mergeFromMessage(this);
|
||||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
EVector copyWith(void Function(EVector) updates) =>
|
||||
super.copyWith((message) => updates(message as EVector)) as EVector;
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
EVector copyWith(void Function(EVector) updates) => super.copyWith((message) => updates(message as EVector)) as EVector;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@@ -57,14 +52,13 @@ class EVector extends $pb.GeneratedMessage {
|
||||
EVector createEmptyInstance() => create();
|
||||
static $pb.PbList<EVector> createRepeated() => $pb.PbList<EVector>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static EVector getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<EVector>(create);
|
||||
static EVector getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<EVector>(create);
|
||||
static EVector? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.List<$core.double> get values => $_getList(0);
|
||||
}
|
||||
|
||||
|
||||
const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names');
|
||||
const _omitMessageNames =
|
||||
$core.bool.fromEnvironment('protobuf.omit_message_names');
|
||||
const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names');
|
||||
|
||||
@@ -8,3 +8,4 @@
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
|
||||
@@ -22,5 +22,6 @@ const EVector$json = {
|
||||
};
|
||||
|
||||
/// Descriptor for `EVector`. Decode as a `google.protobuf.DescriptorProto`.
|
||||
final $typed_data.Uint8List eVectorDescriptor =
|
||||
$convert.base64Decode('CgdFVmVjdG9yEhYKBnZhbHVlcxgBIAMoAVIGdmFsdWVz');
|
||||
final $typed_data.Uint8List eVectorDescriptor = $convert.base64Decode(
|
||||
'CgdFVmVjdG9yEhYKBnZhbHVlcxgBIAMoAVIGdmFsdWVz');
|
||||
|
||||
|
||||
@@ -11,3 +11,4 @@
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
export 'vector.pb.dart';
|
||||
|
||||
|
||||
@@ -31,32 +31,25 @@ class Detection extends $pb.GeneratedMessage {
|
||||
return $result;
|
||||
}
|
||||
Detection._() : super();
|
||||
factory Detection.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
factory Detection.fromJson($core.String i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(i, r);
|
||||
factory Detection.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory Detection.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'Detection',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'),
|
||||
createEmptyInstance: create)
|
||||
..aOM<$0.CenterBox>(1, _omitFieldNames ? '' : 'box',
|
||||
subBuilder: $0.CenterBox.create)
|
||||
..aOM<$1.EPoint>(2, _omitFieldNames ? '' : 'landmarks',
|
||||
subBuilder: $1.EPoint.create)
|
||||
..hasRequiredFields = false;
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Detection', package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'), createEmptyInstance: create)
|
||||
..aOM<$0.CenterBox>(1, _omitFieldNames ? '' : 'box', subBuilder: $0.CenterBox.create)
|
||||
..aOM<$1.EPoint>(2, _omitFieldNames ? '' : 'landmarks', subBuilder: $1.EPoint.create)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
Detection clone() => Detection()..mergeFromMessage(this);
|
||||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
Detection copyWith(void Function(Detection) updates) =>
|
||||
super.copyWith((message) => updates(message as Detection)) as Detection;
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
Detection copyWith(void Function(Detection) updates) => super.copyWith((message) => updates(message as Detection)) as Detection;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@@ -65,17 +58,13 @@ class Detection extends $pb.GeneratedMessage {
|
||||
Detection createEmptyInstance() => create();
|
||||
static $pb.PbList<Detection> createRepeated() => $pb.PbList<Detection>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static Detection getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Detection>(create);
|
||||
static Detection getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Detection>(create);
|
||||
static Detection? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$0.CenterBox get box => $_getN(0);
|
||||
@$pb.TagNumber(1)
|
||||
set box($0.CenterBox v) {
|
||||
setField(1, v);
|
||||
}
|
||||
|
||||
set box($0.CenterBox v) { setField(1, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasBox() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
@@ -86,10 +75,7 @@ class Detection extends $pb.GeneratedMessage {
|
||||
@$pb.TagNumber(2)
|
||||
$1.EPoint get landmarks => $_getN(1);
|
||||
@$pb.TagNumber(2)
|
||||
set landmarks($1.EPoint v) {
|
||||
setField(2, v);
|
||||
}
|
||||
|
||||
set landmarks($1.EPoint v) { setField(2, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasLandmarks() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
@@ -117,33 +103,26 @@ class Face extends $pb.GeneratedMessage {
|
||||
return $result;
|
||||
}
|
||||
Face._() : super();
|
||||
factory Face.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
factory Face.fromJson($core.String i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(i, r);
|
||||
factory Face.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory Face.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'Face',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'),
|
||||
createEmptyInstance: create)
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Face', package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'), createEmptyInstance: create)
|
||||
..aOS(1, _omitFieldNames ? '' : 'id')
|
||||
..aOM<Detection>(2, _omitFieldNames ? '' : 'detection',
|
||||
subBuilder: Detection.create)
|
||||
..a<$core.double>(
|
||||
3, _omitFieldNames ? '' : 'confidence', $pb.PbFieldType.OF)
|
||||
..hasRequiredFields = false;
|
||||
..aOM<Detection>(2, _omitFieldNames ? '' : 'detection', subBuilder: Detection.create)
|
||||
..a<$core.double>(3, _omitFieldNames ? '' : 'confidence', $pb.PbFieldType.OF)
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
Face clone() => Face()..mergeFromMessage(this);
|
||||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
Face copyWith(void Function(Face) updates) =>
|
||||
super.copyWith((message) => updates(message as Face)) as Face;
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
Face copyWith(void Function(Face) updates) => super.copyWith((message) => updates(message as Face)) as Face;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@@ -152,17 +131,13 @@ class Face extends $pb.GeneratedMessage {
|
||||
Face createEmptyInstance() => create();
|
||||
static $pb.PbList<Face> createRepeated() => $pb.PbList<Face>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static Face getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Face>(create);
|
||||
static Face getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Face>(create);
|
||||
static Face? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$core.String get id => $_getSZ(0);
|
||||
@$pb.TagNumber(1)
|
||||
set id($core.String v) {
|
||||
$_setString(0, v);
|
||||
}
|
||||
|
||||
set id($core.String v) { $_setString(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
@@ -171,10 +146,7 @@ class Face extends $pb.GeneratedMessage {
|
||||
@$pb.TagNumber(2)
|
||||
Detection get detection => $_getN(1);
|
||||
@$pb.TagNumber(2)
|
||||
set detection(Detection v) {
|
||||
setField(2, v);
|
||||
}
|
||||
|
||||
set detection(Detection v) { setField(2, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasDetection() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
@@ -185,16 +157,13 @@ class Face extends $pb.GeneratedMessage {
|
||||
@$pb.TagNumber(3)
|
||||
$core.double get confidence => $_getN(2);
|
||||
@$pb.TagNumber(3)
|
||||
set confidence($core.double v) {
|
||||
$_setFloat(2, v);
|
||||
}
|
||||
|
||||
set confidence($core.double v) { $_setFloat(2, v); }
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasConfidence() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
void clearConfidence() => clearField(3);
|
||||
}
|
||||
|
||||
|
||||
const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names');
|
||||
const _omitMessageNames =
|
||||
$core.bool.fromEnvironment('protobuf.omit_message_names');
|
||||
const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names');
|
||||
|
||||
@@ -8,3 +8,4 @@
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
|
||||
@@ -17,26 +17,8 @@ import 'dart:typed_data' as $typed_data;
|
||||
const Detection$json = {
|
||||
'1': 'Detection',
|
||||
'2': [
|
||||
{
|
||||
'1': 'box',
|
||||
'3': 1,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.ente.common.CenterBox',
|
||||
'9': 0,
|
||||
'10': 'box',
|
||||
'17': true
|
||||
},
|
||||
{
|
||||
'1': 'landmarks',
|
||||
'3': 2,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.ente.common.EPoint',
|
||||
'9': 1,
|
||||
'10': 'landmarks',
|
||||
'17': true
|
||||
},
|
||||
{'1': 'box', '3': 1, '4': 1, '5': 11, '6': '.ente.common.CenterBox', '9': 0, '10': 'box', '17': true},
|
||||
{'1': 'landmarks', '3': 2, '4': 1, '5': 11, '6': '.ente.common.EPoint', '9': 1, '10': 'landmarks', '17': true},
|
||||
],
|
||||
'8': [
|
||||
{'1': '_box'},
|
||||
@@ -55,25 +37,8 @@ const Face$json = {
|
||||
'1': 'Face',
|
||||
'2': [
|
||||
{'1': 'id', '3': 1, '4': 1, '5': 9, '9': 0, '10': 'id', '17': true},
|
||||
{
|
||||
'1': 'detection',
|
||||
'3': 2,
|
||||
'4': 1,
|
||||
'5': 11,
|
||||
'6': '.ente.ml.Detection',
|
||||
'9': 1,
|
||||
'10': 'detection',
|
||||
'17': true
|
||||
},
|
||||
{
|
||||
'1': 'confidence',
|
||||
'3': 3,
|
||||
'4': 1,
|
||||
'5': 2,
|
||||
'9': 2,
|
||||
'10': 'confidence',
|
||||
'17': true
|
||||
},
|
||||
{'1': 'detection', '3': 2, '4': 1, '5': 11, '6': '.ente.ml.Detection', '9': 1, '10': 'detection', '17': true},
|
||||
{'1': 'confidence', '3': 3, '4': 1, '5': 2, '9': 2, '10': 'confidence', '17': true},
|
||||
],
|
||||
'8': [
|
||||
{'1': '_id'},
|
||||
@@ -87,3 +52,4 @@ final $typed_data.Uint8List faceDescriptor = $convert.base64Decode(
|
||||
'CgRGYWNlEhMKAmlkGAEgASgJSABSAmlkiAEBEjUKCWRldGVjdGlvbhgCIAEoCzISLmVudGUubW'
|
||||
'wuRGV0ZWN0aW9uSAFSCWRldGVjdGlvbogBARIjCgpjb25maWRlbmNlGAMgASgCSAJSCmNvbmZp'
|
||||
'ZGVuY2WIAQFCBQoDX2lkQgwKCl9kZXRlY3Rpb25CDQoLX2NvbmZpZGVuY2U=');
|
||||
|
||||
|
||||
@@ -11,3 +11,4 @@
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
export 'face.pb.dart';
|
||||
|
||||
|
||||
@@ -31,30 +31,25 @@ class FileML extends $pb.GeneratedMessage {
|
||||
return $result;
|
||||
}
|
||||
FileML._() : super();
|
||||
factory FileML.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
factory FileML.fromJson($core.String i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(i, r);
|
||||
factory FileML.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory FileML.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'FileML',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'),
|
||||
createEmptyInstance: create)
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'FileML', package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'), createEmptyInstance: create)
|
||||
..aInt64(1, _omitFieldNames ? '' : 'id')
|
||||
..p<$core.double>(2, _omitFieldNames ? '' : 'clip', $pb.PbFieldType.KD)
|
||||
..hasRequiredFields = false;
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
FileML clone() => FileML()..mergeFromMessage(this);
|
||||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
FileML copyWith(void Function(FileML) updates) =>
|
||||
super.copyWith((message) => updates(message as FileML)) as FileML;
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
FileML copyWith(void Function(FileML) updates) => super.copyWith((message) => updates(message as FileML)) as FileML;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@@ -63,17 +58,13 @@ class FileML extends $pb.GeneratedMessage {
|
||||
FileML createEmptyInstance() => create();
|
||||
static $pb.PbList<FileML> createRepeated() => $pb.PbList<FileML>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static FileML getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<FileML>(create);
|
||||
static FileML getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<FileML>(create);
|
||||
static FileML? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
$fixnum.Int64 get id => $_getI64(0);
|
||||
@$pb.TagNumber(1)
|
||||
set id($fixnum.Int64 v) {
|
||||
$_setInt64(0, v);
|
||||
}
|
||||
|
||||
set id($fixnum.Int64 v) { $_setInt64(0, v); }
|
||||
@$pb.TagNumber(1)
|
||||
$core.bool hasId() => $_has(0);
|
||||
@$pb.TagNumber(1)
|
||||
@@ -110,34 +101,28 @@ class FileFaces extends $pb.GeneratedMessage {
|
||||
return $result;
|
||||
}
|
||||
FileFaces._() : super();
|
||||
factory FileFaces.fromBuffer($core.List<$core.int> i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromBuffer(i, r);
|
||||
factory FileFaces.fromJson($core.String i,
|
||||
[$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) =>
|
||||
create()..mergeFromJson(i, r);
|
||||
factory FileFaces.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r);
|
||||
factory FileFaces.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r);
|
||||
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(
|
||||
_omitMessageNames ? '' : 'FileFaces',
|
||||
package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'),
|
||||
createEmptyInstance: create)
|
||||
..pc<$2.Face>(1, _omitFieldNames ? '' : 'faces', $pb.PbFieldType.PM,
|
||||
subBuilder: $2.Face.create)
|
||||
static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'FileFaces', package: const $pb.PackageName(_omitMessageNames ? '' : 'ente.ml'), createEmptyInstance: create)
|
||||
..pc<$2.Face>(1, _omitFieldNames ? '' : 'faces', $pb.PbFieldType.PM, subBuilder: $2.Face.create)
|
||||
..a<$core.int>(2, _omitFieldNames ? '' : 'height', $pb.PbFieldType.O3)
|
||||
..a<$core.int>(3, _omitFieldNames ? '' : 'width', $pb.PbFieldType.O3)
|
||||
..a<$core.int>(4, _omitFieldNames ? '' : 'version', $pb.PbFieldType.O3)
|
||||
..aOS(5, _omitFieldNames ? '' : 'error')
|
||||
..hasRequiredFields = false;
|
||||
..hasRequiredFields = false
|
||||
;
|
||||
|
||||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.deepCopy] instead. '
|
||||
'Will be removed in next major version')
|
||||
FileFaces clone() => FileFaces()..mergeFromMessage(this);
|
||||
@$core.Deprecated('Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
FileFaces copyWith(void Function(FileFaces) updates) =>
|
||||
super.copyWith((message) => updates(message as FileFaces)) as FileFaces;
|
||||
@$core.Deprecated(
|
||||
'Using this can add significant overhead to your binary. '
|
||||
'Use [GeneratedMessageGenericExtensions.rebuild] instead. '
|
||||
'Will be removed in next major version')
|
||||
FileFaces copyWith(void Function(FileFaces) updates) => super.copyWith((message) => updates(message as FileFaces)) as FileFaces;
|
||||
|
||||
$pb.BuilderInfo get info_ => _i;
|
||||
|
||||
@@ -146,8 +131,7 @@ class FileFaces extends $pb.GeneratedMessage {
|
||||
FileFaces createEmptyInstance() => create();
|
||||
static $pb.PbList<FileFaces> createRepeated() => $pb.PbList<FileFaces>();
|
||||
@$core.pragma('dart2js:noInline')
|
||||
static FileFaces getDefault() =>
|
||||
_defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<FileFaces>(create);
|
||||
static FileFaces getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<FileFaces>(create);
|
||||
static FileFaces? _defaultInstance;
|
||||
|
||||
@$pb.TagNumber(1)
|
||||
@@ -156,10 +140,7 @@ class FileFaces extends $pb.GeneratedMessage {
|
||||
@$pb.TagNumber(2)
|
||||
$core.int get height => $_getIZ(1);
|
||||
@$pb.TagNumber(2)
|
||||
set height($core.int v) {
|
||||
$_setSignedInt32(1, v);
|
||||
}
|
||||
|
||||
set height($core.int v) { $_setSignedInt32(1, v); }
|
||||
@$pb.TagNumber(2)
|
||||
$core.bool hasHeight() => $_has(1);
|
||||
@$pb.TagNumber(2)
|
||||
@@ -168,10 +149,7 @@ class FileFaces extends $pb.GeneratedMessage {
|
||||
@$pb.TagNumber(3)
|
||||
$core.int get width => $_getIZ(2);
|
||||
@$pb.TagNumber(3)
|
||||
set width($core.int v) {
|
||||
$_setSignedInt32(2, v);
|
||||
}
|
||||
|
||||
set width($core.int v) { $_setSignedInt32(2, v); }
|
||||
@$pb.TagNumber(3)
|
||||
$core.bool hasWidth() => $_has(2);
|
||||
@$pb.TagNumber(3)
|
||||
@@ -180,10 +158,7 @@ class FileFaces extends $pb.GeneratedMessage {
|
||||
@$pb.TagNumber(4)
|
||||
$core.int get version => $_getIZ(3);
|
||||
@$pb.TagNumber(4)
|
||||
set version($core.int v) {
|
||||
$_setSignedInt32(3, v);
|
||||
}
|
||||
|
||||
set version($core.int v) { $_setSignedInt32(3, v); }
|
||||
@$pb.TagNumber(4)
|
||||
$core.bool hasVersion() => $_has(3);
|
||||
@$pb.TagNumber(4)
|
||||
@@ -192,16 +167,13 @@ class FileFaces extends $pb.GeneratedMessage {
|
||||
@$pb.TagNumber(5)
|
||||
$core.String get error => $_getSZ(4);
|
||||
@$pb.TagNumber(5)
|
||||
set error($core.String v) {
|
||||
$_setString(4, v);
|
||||
}
|
||||
|
||||
set error($core.String v) { $_setString(4, v); }
|
||||
@$pb.TagNumber(5)
|
||||
$core.bool hasError() => $_has(4);
|
||||
@$pb.TagNumber(5)
|
||||
void clearError() => clearField(5);
|
||||
}
|
||||
|
||||
|
||||
const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names');
|
||||
const _omitMessageNames =
|
||||
$core.bool.fromEnvironment('protobuf.omit_message_names');
|
||||
const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names');
|
||||
|
||||
@@ -8,3 +8,4 @@
|
||||
// ignore_for_file: constant_identifier_names, library_prefixes
|
||||
// ignore_for_file: non_constant_identifier_names, prefer_final_fields
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
|
||||
@@ -34,25 +34,10 @@ final $typed_data.Uint8List fileMLDescriptor = $convert.base64Decode(
|
||||
const FileFaces$json = {
|
||||
'1': 'FileFaces',
|
||||
'2': [
|
||||
{
|
||||
'1': 'faces',
|
||||
'3': 1,
|
||||
'4': 3,
|
||||
'5': 11,
|
||||
'6': '.ente.ml.Face',
|
||||
'10': 'faces'
|
||||
},
|
||||
{'1': 'faces', '3': 1, '4': 3, '5': 11, '6': '.ente.ml.Face', '10': 'faces'},
|
||||
{'1': 'height', '3': 2, '4': 1, '5': 5, '9': 0, '10': 'height', '17': true},
|
||||
{'1': 'width', '3': 3, '4': 1, '5': 5, '9': 1, '10': 'width', '17': true},
|
||||
{
|
||||
'1': 'version',
|
||||
'3': 4,
|
||||
'4': 1,
|
||||
'5': 5,
|
||||
'9': 2,
|
||||
'10': 'version',
|
||||
'17': true
|
||||
},
|
||||
{'1': 'version', '3': 4, '4': 1, '5': 5, '9': 2, '10': 'version', '17': true},
|
||||
{'1': 'error', '3': 5, '4': 1, '5': 9, '9': 3, '10': 'error', '17': true},
|
||||
],
|
||||
'8': [
|
||||
@@ -69,3 +54,4 @@ final $typed_data.Uint8List fileFacesDescriptor = $convert.base64Decode(
|
||||
'dodBgCIAEoBUgAUgZoZWlnaHSIAQESGQoFd2lkdGgYAyABKAVIAVIFd2lkdGiIAQESHQoHdmVy'
|
||||
'c2lvbhgEIAEoBUgCUgd2ZXJzaW9uiAEBEhkKBWVycm9yGAUgASgJSANSBWVycm9yiAEBQgkKB1'
|
||||
'9oZWlnaHRCCAoGX3dpZHRoQgoKCF92ZXJzaW9uQggKBl9lcnJvcg==');
|
||||
|
||||
|
||||
@@ -11,3 +11,4 @@
|
||||
// ignore_for_file: unnecessary_import, unnecessary_this, unused_import
|
||||
|
||||
export 'fileml.pb.dart';
|
||||
|
||||
|
||||
@@ -1602,6 +1602,7 @@
|
||||
"processing": "Przetwarzanie",
|
||||
"queued": "W kolejce",
|
||||
"ineligible": "Nie kwalifikuje się",
|
||||
"failed": "Nie powiodło się",
|
||||
"playOriginal": "Odtwórz oryginał",
|
||||
"joinAlbumConfirmationDialogBody": "Dołączenie do albumu sprawi, że Twój e-mail będzie widoczny dla jego uczestników.",
|
||||
"pleaseWaitThisWillTakeAWhile": "Prosimy czekać, to może zająć chwilę.",
|
||||
@@ -1621,6 +1622,7 @@
|
||||
"appIcon": "Ikona aplikacji",
|
||||
"notThisPerson": "Nie ta osoba?",
|
||||
"selectedItemsWillBeRemovedFromThisPerson": "Wybrane elementy zostaną usunięte z tej osoby, ale nie zostaną usunięte z Twojej biblioteki.",
|
||||
"throughTheYears": "{dateFormat} przez lata",
|
||||
"thisWeekThroughTheYears": "Ten tydzień przez lata",
|
||||
"youAndThem": "Ty i {name}",
|
||||
"admiringThem": "Podziwianie {name}",
|
||||
@@ -1705,5 +1707,7 @@
|
||||
"cLDesc5": "Od teraz otrzymasz powiadomienie z możliwością rezygnacji dotyczące wszystkich zapisanych urodzin w Ente, wraz z kolekcją najlepszych zdjęć danej osoby.",
|
||||
"cLTitle6": "Wznawialne Przesyłanie i Pobieranie Danych",
|
||||
"cLDesc6": "Nie musisz już czekać na zakończenie przesyłania ani pobierania, żeby móc zamknąć aplikację. Wszystkie operacje przesyłania i pobierania można teraz wstrzymać w dowolnym momencie i wznowić od miejsca, w którym zostały przerwane.",
|
||||
"indexingPausedStatusDescription": "Indeksowanie zostało wstrzymane. Zostanie automatycznie wznowione, gdy urządzenie będzie gotowe. Urządzenie uznaje się za gotowe, gdy poziom baterii, stan jej zdrowia oraz status termiczny znajdują się w bezpiecznym zakresie."
|
||||
"indexingPausedStatusDescription": "Indeksowanie zostało wstrzymane. Zostanie automatycznie wznowione, gdy urządzenie będzie gotowe. Urządzenie uznaje się za gotowe, gdy poziom baterii, stan jej zdrowia oraz status termiczny znajdują się w bezpiecznym zakresie.",
|
||||
"faceThumbnailGenerationFailed": "Nie można wygenerować miniaturek twarzy",
|
||||
"fileAnalysisFailed": "Nie można przeanalizować pliku"
|
||||
}
|
||||
@@ -1788,5 +1788,7 @@
|
||||
"cLDesc5": "Теперь вы будете получать уведомления о всех днях рождениях, которые вы сохранили на Ente, а также коллекцию их лучших фотографий.",
|
||||
"cLTitle6": "Возобновляемые загрузки и скачивания",
|
||||
"cLDesc6": "Больше не нужно ждать завершения загрузки/скачивания, прежде чем закрыть приложение. Все загрузки и скачивания теперь можно приостановить и возобновить с того места, где вы остановились.",
|
||||
"indexingPausedStatusDescription": "Индексирование приостановлено. Оно автоматически возобновится, когда устройство будет готово. Устройство считается готовым, когда уровень заряда батареи, её состояние и температура находятся в пределах нормы."
|
||||
"indexingPausedStatusDescription": "Индексирование приостановлено. Оно автоматически возобновится, когда устройство будет готово. Устройство считается готовым, когда уровень заряда батареи, её состояние и температура находятся в пределах нормы.",
|
||||
"faceThumbnailGenerationFailed": "Не удалось создать миниатюры лиц",
|
||||
"fileAnalysisFailed": "Не удалось проанализировать файл"
|
||||
}
|
||||
@@ -825,8 +825,8 @@
|
||||
"doubleYourStorage": "Gấp đôi dung lượng lưu trữ của bạn",
|
||||
"referFriendsAnd2xYourPlan": "Giới thiệu bạn bè và ×2 gói của bạn",
|
||||
"shareAlbumHint": "Mở album và nhấn nút chia sẻ ở góc trên bên phải để chia sẻ.",
|
||||
"itemsShowTheNumberOfDaysRemainingBeforePermanentDeletion": "Các mục hiện số ngày còn lại trước khi xóa vĩnh viễn",
|
||||
"trashDaysLeft": "{count, plural, =0 {Sắp tới} =1 {1 ngày} other {{count} ngày}}",
|
||||
"itemsShowTheNumberOfDaysRemainingBeforePermanentDeletion": "Trên các mục là số ngày còn lại trước khi xóa vĩnh viễn",
|
||||
"trashDaysLeft": "{count, plural, =0 {Sắp xóa} =1 {1 ngày} other {{count} ngày}}",
|
||||
"@trashDaysLeft": {
|
||||
"description": "Text to indicate number of days remaining before permanent deletion",
|
||||
"placeholders": {
|
||||
@@ -1178,7 +1178,7 @@
|
||||
"addPhotos": "Thêm ảnh",
|
||||
"noPhotosFoundHere": "Không tìm thấy ảnh ở đây",
|
||||
"zoomOutToSeePhotos": "Phóng to để xem ảnh",
|
||||
"noImagesWithLocation": "Không có ảnh ở vị trí này",
|
||||
"noImagesWithLocation": "Không có ảnh với vị trí",
|
||||
"unpinAlbum": "Bỏ ghim album",
|
||||
"pinAlbum": "Ghim album",
|
||||
"create": "Tạo",
|
||||
|
||||
@@ -1788,5 +1788,7 @@
|
||||
"cLDesc5": "您现在将收到 Ente 上保存的所有生日的可选退出通知,同时附上他们最佳照片的合集。",
|
||||
"cLTitle6": "可恢复的上传和下载",
|
||||
"cLDesc6": "无需等待上传/下载完成即可关闭应用程序。所有上传和下载现在都可以中途暂停,并从中断处继续。",
|
||||
"indexingPausedStatusDescription": "索引已暂停。待设备准备就绪后,索引将自动恢复。当设备的电池电量、电池健康度和温度状态处于健康范围内时,设备即被视为准备就绪。"
|
||||
"indexingPausedStatusDescription": "索引已暂停。待设备准备就绪后,索引将自动恢复。当设备的电池电量、电池健康度和温度状态处于健康范围内时,设备即被视为准备就绪。",
|
||||
"faceThumbnailGenerationFailed": "无法生成人脸缩略图",
|
||||
"fileAnalysisFailed": "无法分析文件"
|
||||
}
|
||||
@@ -43,7 +43,6 @@ import 'package:photos/services/sync/remote_sync_service.dart';
|
||||
import "package:photos/services/sync/sync_service.dart";
|
||||
import "package:photos/services/video_preview_service.dart";
|
||||
import "package:photos/services/wake_lock_service.dart";
|
||||
import "package:photos/src/rust/frb_generated.dart";
|
||||
import 'package:photos/ui/tools/app_lock.dart';
|
||||
import 'package:photos/ui/tools/lock_screen.dart';
|
||||
import "package:photos/utils/email_util.dart";
|
||||
@@ -64,7 +63,6 @@ const kFGTaskDeathTimeoutInMicroseconds = 5000000;
|
||||
|
||||
void main() async {
|
||||
debugRepaintRainbowEnabled = false;
|
||||
await RustLib.init();
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
MediaKit.ensureInitialized();
|
||||
|
||||
|
||||
@@ -1427,12 +1427,20 @@ class CollectionsService {
|
||||
}
|
||||
// group files by collectionID
|
||||
final Map<int, List<EnteFile>> filesByCollection = {};
|
||||
final Map<int, Set<int>> fileSeenByCollection = {};
|
||||
for (final file in filesToCopy) {
|
||||
if (filesByCollection.containsKey(file.collectionID!)) {
|
||||
filesByCollection[file.collectionID!]!.add(file.copyWith());
|
||||
} else {
|
||||
filesByCollection[file.collectionID!] = [file.copyWith()];
|
||||
fileSeenByCollection.putIfAbsent(file.collectionID!, () => <int>{});
|
||||
if (fileSeenByCollection[file.collectionID]!
|
||||
.contains(file.uploadedFileID)) {
|
||||
_logger.warning(
|
||||
"skip copy, duplicate ID: ${file.uploadedFileID} in collection "
|
||||
"${file.collectionID}",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
filesByCollection
|
||||
.putIfAbsent(file.collectionID!, () => [])
|
||||
.add(file.copyWith());
|
||||
}
|
||||
for (final entry in filesByCollection.entries) {
|
||||
final srcCollectionID = entry.key;
|
||||
@@ -1579,9 +1587,6 @@ class CollectionsService {
|
||||
params["files"] = [];
|
||||
for (final batchFile in batch) {
|
||||
final fileKey = getFileKey(batchFile);
|
||||
_logger.info(
|
||||
"srcCollection : $srcCollectionID file: ${batchFile.uploadedFileID} key: ${CryptoUtil.bin2base64(fileKey)} ",
|
||||
);
|
||||
final encryptedKeyData =
|
||||
CryptoUtil.encryptSync(fileKey, getCollectionKey(dstCollectionID));
|
||||
batchFile.encryptedKey =
|
||||
@@ -1643,17 +1648,27 @@ class CollectionsService {
|
||||
);
|
||||
final List<EnteFile> filesToCopy = [];
|
||||
final List<EnteFile> filesToAdd = [];
|
||||
final Set<int> seenForAdd = {};
|
||||
final Set<int> seenForCopy = {};
|
||||
|
||||
for (final EnteFile file in othersFile) {
|
||||
if (hashToUserFile.containsKey(file.hash ?? '')) {
|
||||
final userFile = hashToUserFile[file.hash]!;
|
||||
if (userFile.fileType == file.fileType) {
|
||||
filesToAdd.add(userFile);
|
||||
} else {
|
||||
filesToCopy.add(file);
|
||||
}
|
||||
} else {
|
||||
filesToCopy.add(file);
|
||||
final userFile = hashToUserFile[file.hash ?? ''];
|
||||
final bool shouldAdd =
|
||||
userFile != null && userFile.fileType == file.fileType;
|
||||
final targetList = shouldAdd ? filesToAdd : filesToCopy;
|
||||
final seenSet = shouldAdd ? seenForAdd : seenForCopy;
|
||||
final fileToProcess = shouldAdd ? userFile : file;
|
||||
final uploadID = fileToProcess.uploadedFileID;
|
||||
|
||||
if (seenSet.contains(uploadID)) {
|
||||
final action = shouldAdd ? "adding" : "copying";
|
||||
_logger.warning(
|
||||
"skip $action file $uploadID as it is already ${action}ed",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
targetList.add(fileToProcess);
|
||||
seenSet.add(uploadID!);
|
||||
}
|
||||
return (filesToAdd, filesToCopy);
|
||||
}
|
||||
|
||||
@@ -8,11 +8,11 @@ import "package:ml_linalg/dtype.dart";
|
||||
import "package:ml_linalg/vector.dart";
|
||||
import "package:photos/generated/protos/ente/common/vector.pb.dart";
|
||||
import "package:photos/models/base/id.dart";
|
||||
import "package:photos/services/isolate_functions.dart";
|
||||
import "package:photos/services/isolate_service.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/face_clustering/face_db_info_for_clustering.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/face_filtering/face_filtering_constants.dart";
|
||||
import "package:photos/services/machine_learning/ml_result.dart";
|
||||
import "package:photos/utils/isolate/isolate_operations.dart";
|
||||
import "package:photos/utils/isolate/super_isolate.dart";
|
||||
|
||||
class FaceInfo {
|
||||
final String faceID;
|
||||
|
||||
@@ -89,8 +89,8 @@ class FaceDetectionRelative extends Detection {
|
||||
// Calculate the scaling
|
||||
final double scaleX = originalSize.width / newSize.width;
|
||||
final double scaleY = originalSize.height / newSize.height;
|
||||
const double translateX = 0;
|
||||
const double translateY = 0;
|
||||
final double translateX = - ((originalSize.width - newSize.width) ~/ 2) / originalSize.width;
|
||||
final double translateY = - ((originalSize.height - newSize.height) ~/ 2) / originalSize.height;
|
||||
|
||||
// Transform Box
|
||||
_transformBox(box, scaleX, scaleY, translateX, translateY);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import "dart:async";
|
||||
import 'dart:typed_data' show Float32List, Uint8List;
|
||||
import 'dart:ui' as ui show Image;
|
||||
|
||||
import 'package:logging/logging.dart';
|
||||
import "package:onnx_dart/onnx_dart.dart";
|
||||
@@ -44,8 +43,9 @@ class FaceDetectionService extends MlModel {
|
||||
|
||||
/// Detects faces in the given image data.
|
||||
static Future<List<FaceDetectionRelative>> predict(
|
||||
ui.Image image,
|
||||
Uint8List rawRgbaBytes,
|
||||
Uint8List resizedBytes,
|
||||
int resizedHeight,
|
||||
int resizedWidth,
|
||||
int sessionAddress,
|
||||
) async {
|
||||
assert(
|
||||
@@ -57,9 +57,11 @@ class FaceDetectionService extends MlModel {
|
||||
|
||||
final startTime = DateTime.now();
|
||||
|
||||
final (inputImageList, scaledSize) = await preprocessImageYoloFace(
|
||||
image,
|
||||
rawRgbaBytes,
|
||||
final scaledSize = Dimensions(width: resizedWidth, height: resizedHeight);
|
||||
final inputImageList = await resizedToPreprocessedYoloFace(
|
||||
resizedBytes,
|
||||
scaledSize.width,
|
||||
scaledSize.height,
|
||||
);
|
||||
final preprocessingTime = DateTime.now();
|
||||
final preprocessingMs =
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import "dart:async" show unawaited;
|
||||
import "dart:typed_data" show Uint8List, Float32List;
|
||||
import "dart:ui" show Image;
|
||||
|
||||
import "package:logging/logging.dart";
|
||||
import "package:photos/core/event_bus.dart";
|
||||
import "package:photos/events/diff_sync_complete_event.dart";
|
||||
import "package:photos/events/people_changed_event.dart";
|
||||
import "package:photos/models/ml/face/dimension.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/face_detection/detection.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/face_detection/face_detection_service.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/face_embedding/face_embedding_service.dart";
|
||||
@@ -73,8 +73,11 @@ class FaceRecognitionService {
|
||||
|
||||
static Future<List<FaceResult>> runFacesPipeline(
|
||||
int enteFileID,
|
||||
Image image,
|
||||
Dimensions imageDimensions,
|
||||
Uint8List rawRgbaBytes,
|
||||
Uint8List resizedRgbBytes,
|
||||
int resizedHeight,
|
||||
int resizedWidth,
|
||||
int faceDetectionAddress,
|
||||
int faceEmbeddingAddress,
|
||||
) async {
|
||||
@@ -85,8 +88,9 @@ class FaceRecognitionService {
|
||||
final List<FaceDetectionRelative> faceDetectionResult =
|
||||
await _detectFacesSync(
|
||||
enteFileID,
|
||||
image,
|
||||
rawRgbaBytes,
|
||||
resizedRgbBytes,
|
||||
resizedHeight,
|
||||
resizedWidth,
|
||||
faceDetectionAddress,
|
||||
faceResults,
|
||||
);
|
||||
@@ -103,7 +107,7 @@ class FaceRecognitionService {
|
||||
|
||||
// Align the faces
|
||||
final Float32List faceAlignmentResult = await _alignFacesSync(
|
||||
image,
|
||||
imageDimensions,
|
||||
rawRgbaBytes,
|
||||
faceDetectionResult,
|
||||
faceResults,
|
||||
@@ -133,8 +137,9 @@ class FaceRecognitionService {
|
||||
/// Runs face recognition on the given image data.
|
||||
static Future<List<FaceDetectionRelative>> _detectFacesSync(
|
||||
int fileID,
|
||||
Image image,
|
||||
Uint8List rawRgbaBytes,
|
||||
Uint8List resizedBytes,
|
||||
int resizedHeight,
|
||||
int resizedWidth,
|
||||
int interpreterAddress,
|
||||
List<FaceResult> faceResults,
|
||||
) async {
|
||||
@@ -142,8 +147,9 @@ class FaceRecognitionService {
|
||||
// Get the bounding boxes of the faces
|
||||
final List<FaceDetectionRelative> faces =
|
||||
await FaceDetectionService.predict(
|
||||
image,
|
||||
rawRgbaBytes,
|
||||
resizedBytes,
|
||||
resizedHeight,
|
||||
resizedWidth,
|
||||
interpreterAddress,
|
||||
);
|
||||
|
||||
@@ -169,7 +175,7 @@ class FaceRecognitionService {
|
||||
/// Aligns multiple faces from the given image data.
|
||||
/// Returns a list of the aligned faces as image data.
|
||||
static Future<Float32List> _alignFacesSync(
|
||||
Image image,
|
||||
Dimensions imageDimensions,
|
||||
Uint8List rawRgbaBytes,
|
||||
List<FaceDetectionRelative> faces,
|
||||
List<FaceResult> faceResults,
|
||||
@@ -177,7 +183,7 @@ class FaceRecognitionService {
|
||||
try {
|
||||
final (alignedFaces, alignmentResults, _, blurValues, _) =
|
||||
await preprocessToMobileFaceNetFloat32List(
|
||||
image,
|
||||
imageDimensions,
|
||||
rawRgbaBytes,
|
||||
faces,
|
||||
);
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data' show Uint8List;
|
||||
|
||||
import "package:computer/computer.dart";
|
||||
import "package:logging/logging.dart";
|
||||
import "package:photos/models/ml/face/box.dart";
|
||||
import "package:photos/services/isolate_functions.dart";
|
||||
import "package:photos/services/isolate_service.dart";
|
||||
import "package:photos/utils/image_ml_util.dart";
|
||||
import "package:photos/utils/isolate/isolate_operations.dart";
|
||||
import "package:photos/utils/isolate/super_isolate.dart";
|
||||
|
||||
final Computer _computer = Computer.shared();
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
class FaceThumbnailGenerator extends SuperIsolate {
|
||||
@override
|
||||
Logger get logger => _logger;
|
||||
@@ -37,20 +35,30 @@ class FaceThumbnailGenerator extends SuperIsolate {
|
||||
String imagePath,
|
||||
List<FaceBox> faceBoxes,
|
||||
) async {
|
||||
final List<Map<String, dynamic>> faceBoxesJson =
|
||||
faceBoxes.map((box) => box.toJson()).toList();
|
||||
final List<Uint8List> faces = await runInIsolate(
|
||||
IsolateOperation.generateFaceThumbnails,
|
||||
{
|
||||
'imagePath': imagePath,
|
||||
'faceBoxesList': faceBoxesJson,
|
||||
},
|
||||
).then((value) => value.cast<Uint8List>());
|
||||
final compressedFaces =
|
||||
await compressFaceThumbnails({'listPngBytes': faces});
|
||||
_logger.fine(
|
||||
"Compressed face thumbnails from sizes ${faces.map((e) => e.length / 1024).toList()} to ${compressedFaces.map((e) => e.length / 1024).toList()} kilobytes",
|
||||
);
|
||||
return compressedFaces;
|
||||
try {
|
||||
_logger.info(
|
||||
"Generating face thumbnails for ${faceBoxes.length} face boxes in $imagePath",
|
||||
);
|
||||
final List<Map<String, dynamic>> faceBoxesJson =
|
||||
faceBoxes.map((box) => box.toJson()).toList();
|
||||
final List<Uint8List> faces = await runInIsolate(
|
||||
IsolateOperation.generateFaceThumbnails,
|
||||
{
|
||||
'imagePath': imagePath,
|
||||
'faceBoxesList': faceBoxesJson,
|
||||
},
|
||||
).then((value) => value.cast<Uint8List>());
|
||||
_logger.info("Generated face thumbnails");
|
||||
final compressedFaces =
|
||||
await compressFaceThumbnails({'listPngBytes': faces});
|
||||
_logger.fine(
|
||||
"Compressed face thumbnails from sizes ${faces.map((e) => e.length / 1024).toList()} to ${compressedFaces.map((e) => e.length / 1024).toList()} kilobytes",
|
||||
);
|
||||
return compressedFaces;
|
||||
} catch (e, s) {
|
||||
_logger.severe("Failed to generate face thumbnails", e, s);
|
||||
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,12 @@ import 'dart:async';
|
||||
|
||||
import "package:logging/logging.dart";
|
||||
import "package:photos/models/ml/vector.dart";
|
||||
import "package:photos/services/isolate_functions.dart";
|
||||
import "package:photos/services/isolate_service.dart";
|
||||
import "package:photos/services/machine_learning/ml_constants.dart";
|
||||
import "package:photos/services/machine_learning/semantic_search/clip/clip_text_encoder.dart";
|
||||
import "package:photos/services/machine_learning/semantic_search/query_result.dart";
|
||||
import "package:photos/services/remote_assets_service.dart";
|
||||
import "package:photos/utils/isolate/isolate_operations.dart";
|
||||
import "package:photos/utils/isolate/super_isolate.dart";
|
||||
import "package:synchronized/synchronized.dart";
|
||||
|
||||
class MLComputer extends SuperIsolate {
|
||||
|
||||
@@ -1 +1 @@
|
||||
const imageEmbeddingsKey = "imageEmbeddings";
|
||||
const imageEmbeddingsKey = "imageEmbeddings";
|
||||
@@ -2,14 +2,14 @@ import "dart:async";
|
||||
|
||||
import "package:flutter/foundation.dart" show debugPrint;
|
||||
import "package:logging/logging.dart";
|
||||
import "package:photos/services/isolate_functions.dart";
|
||||
import "package:photos/services/isolate_service.dart";
|
||||
import 'package:photos/services/machine_learning/face_ml/face_detection/face_detection_service.dart';
|
||||
import 'package:photos/services/machine_learning/face_ml/face_embedding/face_embedding_service.dart';
|
||||
import "package:photos/services/machine_learning/ml_models_overview.dart";
|
||||
import 'package:photos/services/machine_learning/ml_result.dart';
|
||||
import "package:photos/services/machine_learning/semantic_search/clip/clip_image_encoder.dart";
|
||||
import "package:photos/services/remote_assets_service.dart";
|
||||
import "package:photos/utils/isolate/isolate_operations.dart";
|
||||
import "package:photos/utils/isolate/super_isolate.dart";
|
||||
import "package:photos/utils/ml_util.dart";
|
||||
import "package:photos/utils/network_util.dart";
|
||||
import "package:synchronized/synchronized.dart";
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import "dart:typed_data" show Uint8List, Float32List;
|
||||
import "dart:ui" show Image;
|
||||
|
||||
import "package:logging/logging.dart";
|
||||
import "package:onnx_dart/onnx_dart.dart";
|
||||
@@ -28,16 +27,23 @@ class ClipImageEncoder extends MlModel {
|
||||
factory ClipImageEncoder() => instance;
|
||||
|
||||
static Future<List<double>> predict(
|
||||
Image image,
|
||||
Uint8List rawRgbaBytes,
|
||||
Uint8List resizedBytes,
|
||||
int resizedHeight,
|
||||
int resizedWidth,
|
||||
int sessionAddress, [
|
||||
int? enteFileID,
|
||||
]) async {
|
||||
final startTime = DateTime.now();
|
||||
final inputList = await preprocessImageClip(image, rawRgbaBytes);
|
||||
|
||||
final inputList = await resizedToPreprocessedClip(
|
||||
resizedBytes,
|
||||
resizedWidth,
|
||||
resizedHeight,
|
||||
);
|
||||
final preprocessingTime = DateTime.now();
|
||||
final preprocessingMs =
|
||||
preprocessingTime.difference(startTime).inMilliseconds;
|
||||
|
||||
late List<double> result;
|
||||
try {
|
||||
if (MlModel.usePlatformPlugin) {
|
||||
@@ -57,7 +63,7 @@ class ClipImageEncoder extends MlModel {
|
||||
final inferenceMs = inferTime.difference(preprocessingTime).inMilliseconds;
|
||||
final totalMs = inferTime.difference(startTime).inMilliseconds;
|
||||
_logger.info(
|
||||
"Clip image predict took $totalMs ms${enteFileID != null ? " with fileID $enteFileID" : ""} (inference: $inferenceMs ms, preprocessing: $preprocessingMs ms)",
|
||||
"Clip image predict took $totalMs ms${enteFileID != null ? " with fileID $enteFileID" : ""} (preprocessing: $preprocessingMs ms, inference: $inferenceMs ms)",
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -3,4 +3,4 @@ class QueryResult {
|
||||
final double score;
|
||||
|
||||
QueryResult(this.id, this.score);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,12 @@
|
||||
import "dart:async" show Timer, unawaited;
|
||||
import "dart:developer" as dev show log;
|
||||
import "dart:math" show min;
|
||||
import "dart:ui" show Image;
|
||||
|
||||
import "package:flutter/foundation.dart";
|
||||
import "package:logging/logging.dart";
|
||||
import "package:photos/core/cache/lru_map.dart";
|
||||
import "package:photos/core/event_bus.dart";
|
||||
import "package:photos/db/files_db.dart";
|
||||
import "package:photos/db/ml/clip_vector_db.dart";
|
||||
import "package:photos/db/ml/db.dart";
|
||||
import 'package:photos/events/embedding_updated_event.dart';
|
||||
import "package:photos/models/file/file.dart";
|
||||
@@ -266,25 +264,10 @@ class SemanticSearchService {
|
||||
required Map<String, double> minimumSimilarityMap,
|
||||
}) async {
|
||||
final startTime = DateTime.now();
|
||||
if (kDebugMode) {
|
||||
for (final queryText in textQueryToEmbeddingMap.keys) {
|
||||
final embedding = textQueryToEmbeddingMap[queryText]!;
|
||||
dev.log("CLIPTEXT Query: $queryText, embedding: $embedding");
|
||||
}
|
||||
}
|
||||
late final Map<String, List<QueryResult>> queryResults;
|
||||
if (flagService.enableVectorDb) {
|
||||
queryResults = await ClipVectorDB.instance.computeBulkSimilarities(
|
||||
textQueryToEmbeddingMap,
|
||||
minimumSimilarityMap,
|
||||
);
|
||||
} else {
|
||||
await _cacheClipVectors();
|
||||
queryResults = await MLComputer.instance.computeBulkSimilarities(
|
||||
textQueryToEmbeddingMap,
|
||||
minimumSimilarityMap,
|
||||
);
|
||||
}
|
||||
await _cacheClipVectors();
|
||||
final Map<String, List<QueryResult>> queryResults = await MLComputer
|
||||
.instance
|
||||
.computeBulkSimilarities(textQueryToEmbeddingMap, minimumSimilarityMap);
|
||||
final endTime = DateTime.now();
|
||||
_logger.info(
|
||||
"computingSimilarities took for ${textQueryToEmbeddingMap.length} queries " +
|
||||
@@ -310,13 +293,15 @@ class SemanticSearchService {
|
||||
|
||||
static Future<ClipResult> runClipImage(
|
||||
int enteFileID,
|
||||
Image image,
|
||||
Uint8List rawRgbaBytes,
|
||||
Uint8List resizedBytes,
|
||||
int resizedHeight,
|
||||
int resizedWidth,
|
||||
int clipImageAddress,
|
||||
) async {
|
||||
final embedding = await ClipImageEncoder.predict(
|
||||
image,
|
||||
rawRgbaBytes,
|
||||
resizedBytes,
|
||||
resizedHeight,
|
||||
resizedWidth,
|
||||
clipImageAddress,
|
||||
enteFileID,
|
||||
);
|
||||
|
||||
45
mobile/apps/photos/lib/src/rust/api/image_processing.dart
Normal file
@@ -0,0 +1,45 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: require_trailing_commas
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
import 'package:photos/src/rust/frb_generated.dart';
|
||||
|
||||
// These functions are ignored because they are not marked as `pub`: `process_image_ml`
|
||||
|
||||
Future<
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
)> processImageMlFromPath(
|
||||
{required String imagePath}) =>
|
||||
RustLib.instance.api
|
||||
.crateApiImageProcessingProcessImageMlFromPath(imagePath: imagePath);
|
||||
|
||||
Future<
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
)> processImageMlFromData(
|
||||
{required List<int> rgbaData,
|
||||
required int width,
|
||||
required int height}) =>
|
||||
RustLib.instance.api.crateApiImageProcessingProcessImageMlFromData(
|
||||
rgbaData: rgbaData, width: width, height: height);
|
||||
@@ -1,48 +0,0 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: require_trailing_commas
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
import 'package:photos/src/rust/frb_generated.dart';
|
||||
|
||||
// These functions are ignored because they are not marked as `pub`: `ensure_capacity`, `save_index`
|
||||
|
||||
// Rust type: RustOpaqueMoi<flutter_rust_bridge::for_generated::RustAutoOpaqueInner<VectorDB>>
|
||||
abstract class VectorDb implements RustOpaqueInterface {
|
||||
Future<void> addVector({required BigInt key, required List<double> vector});
|
||||
|
||||
Future<void> bulkAddVectors(
|
||||
{required Uint64List keys, required List<Float32List> vectors});
|
||||
|
||||
Future<List<Float32List>> bulkGetVectors({required Uint64List keys});
|
||||
|
||||
Future<BigInt> bulkRemoveVectors({required Uint64List keys});
|
||||
|
||||
Future<(List<Uint64List>, List<Float32List>)> bulkSearchVectors(
|
||||
{required List<Float32List> queries, required BigInt count});
|
||||
|
||||
/// Check if a vector with the given key exists in the index.
|
||||
/// `true` if the index contains the vector with the given key, `false` otherwise.
|
||||
Future<bool> containsVector({required BigInt key});
|
||||
|
||||
Future<void> deleteIndex();
|
||||
|
||||
Future<(BigInt, BigInt, BigInt, BigInt, BigInt, BigInt, BigInt)>
|
||||
getIndexStats();
|
||||
|
||||
Future<Float32List> getVector({required BigInt key});
|
||||
|
||||
factory VectorDb({required String filePath, required BigInt dimensions}) =>
|
||||
RustLib.instance.api.crateApiUsearchApiVectorDbNew(
|
||||
filePath: filePath, dimensions: dimensions);
|
||||
|
||||
Future<BigInt> removeVector({required BigInt key});
|
||||
|
||||
Future<void> resetIndex();
|
||||
|
||||
Future<(Uint64List, Float32List)> searchVectors(
|
||||
{required List<double> query, required BigInt count});
|
||||
}
|
||||
9
mobile/apps/photos/lib/src/rust/custom/init_frb.dart
Normal file
@@ -0,0 +1,9 @@
|
||||
import "package:photos/src/rust/frb_generated.dart";
|
||||
|
||||
bool _isInitFrb = false;
|
||||
|
||||
Future<void> initFrb() async {
|
||||
if (_isInitFrb) return;
|
||||
await RustLib.init();
|
||||
_isInitFrb = true;
|
||||
}
|
||||
@@ -5,14 +5,13 @@
|
||||
|
||||
// ignore_for_file: unused_import, unused_element, unnecessary_import, duplicate_ignore, invalid_use_of_internal_member, annotate_overrides, non_constant_identifier_names, curly_braces_in_flow_control_structures, prefer_const_literals_to_create_immutables, unused_field
|
||||
|
||||
import 'api/image_processing.dart';
|
||||
import 'api/simple.dart';
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi' as ffi;
|
||||
|
||||
import 'frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated_io.dart';
|
||||
import 'package:photos/src/rust/api/simple.dart';
|
||||
import 'package:photos/src/rust/api/usearch_api.dart';
|
||||
import 'package:photos/src/rust/frb_generated.dart';
|
||||
|
||||
abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
RustLibApiImplPlatform({
|
||||
@@ -22,68 +21,31 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
required super.portManager,
|
||||
});
|
||||
|
||||
CrossPlatformFinalizerArg get rust_arc_decrement_strong_count_VectorDbPtr => wire
|
||||
._rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDBPtr;
|
||||
|
||||
@protected
|
||||
VectorDb
|
||||
dco_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB(
|
||||
dynamic raw);
|
||||
|
||||
@protected
|
||||
VectorDb
|
||||
dco_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB(
|
||||
dynamic raw);
|
||||
|
||||
@protected
|
||||
VectorDb
|
||||
dco_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB(
|
||||
dynamic raw);
|
||||
|
||||
@protected
|
||||
String dco_decode_String(dynamic raw);
|
||||
|
||||
@protected
|
||||
bool dco_decode_bool(dynamic raw);
|
||||
|
||||
@protected
|
||||
double dco_decode_f_32(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<Float32List> dco_decode_list_list_prim_f_32_strict(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<Uint64List> dco_decode_list_list_prim_u_64_strict(dynamic raw);
|
||||
|
||||
@protected
|
||||
List<double> dco_decode_list_prim_f_32_loose(dynamic raw);
|
||||
|
||||
@protected
|
||||
Float32List dco_decode_list_prim_f_32_strict(dynamic raw);
|
||||
|
||||
@protected
|
||||
Uint64List dco_decode_list_prim_u_64_strict(dynamic raw);
|
||||
List<int> dco_decode_list_prim_u_8_loose(dynamic raw);
|
||||
|
||||
@protected
|
||||
Uint8List dco_decode_list_prim_u_8_strict(dynamic raw);
|
||||
|
||||
@protected
|
||||
(List<Uint64List>, List<Float32List>)
|
||||
dco_decode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict(
|
||||
dynamic raw);
|
||||
|
||||
@protected
|
||||
(
|
||||
Uint64List,
|
||||
Float32List
|
||||
) dco_decode_record_list_prim_u_64_strict_list_prim_f_32_strict(dynamic raw);
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
) dco_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
|
||||
dynamic raw);
|
||||
|
||||
@protected
|
||||
(BigInt, BigInt, BigInt, BigInt, BigInt, BigInt, BigInt)
|
||||
dco_decode_record_usize_usize_usize_usize_usize_usize_usize(dynamic raw);
|
||||
|
||||
@protected
|
||||
BigInt dco_decode_u_64(dynamic raw);
|
||||
int dco_decode_u_32(dynamic raw);
|
||||
|
||||
@protected
|
||||
int dco_decode_u_8(dynamic raw);
|
||||
@@ -94,67 +56,31 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
@protected
|
||||
BigInt dco_decode_usize(dynamic raw);
|
||||
|
||||
@protected
|
||||
VectorDb
|
||||
sse_decode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB(
|
||||
SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
VectorDb
|
||||
sse_decode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB(
|
||||
SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
VectorDb
|
||||
sse_decode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB(
|
||||
SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
String sse_decode_String(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
bool sse_decode_bool(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
double sse_decode_f_32(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<Float32List> sse_decode_list_list_prim_f_32_strict(
|
||||
SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<Uint64List> sse_decode_list_list_prim_u_64_strict(
|
||||
SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
List<double> sse_decode_list_prim_f_32_loose(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
Float32List sse_decode_list_prim_f_32_strict(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
Uint64List sse_decode_list_prim_u_64_strict(SseDeserializer deserializer);
|
||||
List<int> sse_decode_list_prim_u_8_loose(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
Uint8List sse_decode_list_prim_u_8_strict(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
(List<Uint64List>, List<Float32List>)
|
||||
sse_decode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict(
|
||||
SseDeserializer deserializer);
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
) sse_decode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
|
||||
SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
(Uint64List, Float32List)
|
||||
sse_decode_record_list_prim_u_64_strict_list_prim_f_32_strict(
|
||||
SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
(BigInt, BigInt, BigInt, BigInt, BigInt, BigInt, BigInt)
|
||||
sse_decode_record_usize_usize_usize_usize_usize_usize_usize(
|
||||
SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
BigInt sse_decode_u_64(SseDeserializer deserializer);
|
||||
int sse_decode_u_32(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
int sse_decode_u_8(SseDeserializer deserializer);
|
||||
@@ -169,68 +95,101 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
int sse_decode_i_32(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_Owned_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB(
|
||||
VectorDb self, SseSerializer serializer);
|
||||
bool sse_decode_bool(SseDeserializer deserializer);
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_Auto_Ref_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB(
|
||||
VectorDb self, SseSerializer serializer);
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> cst_encode_String(String raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return cst_encode_list_prim_u_8_strict(utf8.encoder.convert(raw));
|
||||
}
|
||||
|
||||
@protected
|
||||
void
|
||||
sse_encode_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB(
|
||||
VectorDb self, SseSerializer serializer);
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose> cst_encode_list_prim_u_8_loose(
|
||||
List<int> raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
final ans = wire.cst_new_list_prim_u_8_loose(raw.length);
|
||||
ans.ref.ptr.asTypedList(raw.length).setAll(0, raw);
|
||||
return ans;
|
||||
}
|
||||
|
||||
@protected
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> cst_encode_list_prim_u_8_strict(
|
||||
Uint8List raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
final ans = wire.cst_new_list_prim_u_8_strict(raw.length);
|
||||
ans.ref.ptr.asTypedList(raw.length).setAll(0, raw);
|
||||
return ans;
|
||||
}
|
||||
|
||||
@protected
|
||||
int cst_encode_usize(BigInt raw) {
|
||||
// Codec=Cst (C-struct based), see doc to use other codecs
|
||||
return raw.toSigned(64).toInt();
|
||||
}
|
||||
|
||||
@protected
|
||||
void cst_api_fill_to_wire_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
) apiObj,
|
||||
wire_cst_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize
|
||||
wireObj) {
|
||||
wireObj.field0 = cst_encode_list_prim_u_8_strict(apiObj.$1);
|
||||
wireObj.field1 = cst_encode_usize(apiObj.$2);
|
||||
wireObj.field2 = cst_encode_usize(apiObj.$3);
|
||||
wireObj.field3 = cst_encode_list_prim_u_8_strict(apiObj.$4);
|
||||
wireObj.field4 = cst_encode_usize(apiObj.$5);
|
||||
wireObj.field5 = cst_encode_usize(apiObj.$6);
|
||||
wireObj.field6 = cst_encode_list_prim_u_8_strict(apiObj.$7);
|
||||
wireObj.field7 = cst_encode_usize(apiObj.$8);
|
||||
wireObj.field8 = cst_encode_usize(apiObj.$9);
|
||||
}
|
||||
|
||||
@protected
|
||||
int cst_encode_u_32(int raw);
|
||||
|
||||
@protected
|
||||
int cst_encode_u_8(int raw);
|
||||
|
||||
@protected
|
||||
void cst_encode_unit(void raw);
|
||||
|
||||
@protected
|
||||
void sse_encode_String(String self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_bool(bool self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_f_32(double self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_list_prim_f_32_strict(
|
||||
List<Float32List> self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_list_prim_u_64_strict(
|
||||
List<Uint64List> self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_f_32_loose(
|
||||
List<double> self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_f_32_strict(
|
||||
Float32List self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_u_64_strict(
|
||||
Uint64List self, SseSerializer serializer);
|
||||
void sse_encode_list_prim_u_8_loose(List<int> self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_list_prim_u_8_strict(
|
||||
Uint8List self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_record_list_list_prim_u_64_strict_list_list_prim_f_32_strict(
|
||||
(List<Uint64List>, List<Float32List>) self, SseSerializer serializer);
|
||||
void
|
||||
sse_encode_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize(
|
||||
(
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
) self,
|
||||
SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_record_list_prim_u_64_strict_list_prim_f_32_strict(
|
||||
(Uint64List, Float32List) self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_record_usize_usize_usize_usize_usize_usize_usize(
|
||||
(BigInt, BigInt, BigInt, BigInt, BigInt, BigInt, BigInt) self,
|
||||
SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_u_64(BigInt self, SseSerializer serializer);
|
||||
void sse_encode_u_32(int self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_u_8(int self, SseSerializer serializer);
|
||||
@@ -243,10 +202,20 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
|
||||
@protected
|
||||
void sse_encode_i_32(int self, SseSerializer serializer);
|
||||
|
||||
@protected
|
||||
void sse_encode_bool(bool self, SseSerializer serializer);
|
||||
}
|
||||
|
||||
// Section: wire_class
|
||||
|
||||
// ignore_for_file: camel_case_types, non_constant_identifier_names, avoid_positional_boolean_parameters, annotate_overrides, constant_identifier_names
|
||||
// AUTO GENERATED FILE, DO NOT EDIT.
|
||||
//
|
||||
// Generated by `package:ffigen`.
|
||||
// ignore_for_file: type=lint
|
||||
|
||||
/// generated by flutter_rust_bridge
|
||||
class RustLibWire implements BaseWire {
|
||||
factory RustLibWire.fromExternalLibrary(ExternalLibrary lib) =>
|
||||
RustLibWire(lib.ffiDynamicLibrary);
|
||||
@@ -259,35 +228,193 @@ class RustLibWire implements BaseWire {
|
||||
RustLibWire(ffi.DynamicLibrary dynamicLibrary)
|
||||
: _lookup = dynamicLibrary.lookup;
|
||||
|
||||
void
|
||||
rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB(
|
||||
ffi.Pointer<ffi.Void> ptr,
|
||||
/// The symbols are looked up with [lookup].
|
||||
RustLibWire.fromLookup(
|
||||
ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName) lookup,
|
||||
) : _lookup = lookup;
|
||||
|
||||
void store_dart_post_cobject(DartPostCObjectFnType ptr) {
|
||||
return _store_dart_post_cobject(ptr);
|
||||
}
|
||||
|
||||
late final _store_dart_post_cobjectPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(DartPostCObjectFnType)>>(
|
||||
'store_dart_post_cobject',
|
||||
);
|
||||
late final _store_dart_post_cobject = _store_dart_post_cobjectPtr
|
||||
.asFunction<void Function(DartPostCObjectFnType)>();
|
||||
|
||||
WireSyncRust2DartDco wire__crate__api__simple__greet(
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> name,
|
||||
) {
|
||||
return _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB(
|
||||
ptr,
|
||||
return _wire__crate__api__simple__greet(name);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__simple__greetPtr = _lookup<
|
||||
ffi.NativeFunction<
|
||||
WireSyncRust2DartDco Function(
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>)>>(
|
||||
'frbgen_photos_wire__crate__api__simple__greet');
|
||||
late final _wire__crate__api__simple__greet =
|
||||
_wire__crate__api__simple__greetPtr.asFunction<
|
||||
WireSyncRust2DartDco Function(
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)>();
|
||||
|
||||
void wire__crate__api__simple__init_app(int port_) {
|
||||
return _wire__crate__api__simple__init_app(port_);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__simple__init_appPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
|
||||
'frbgen_photos_wire__crate__api__simple__init_app',
|
||||
);
|
||||
late final _wire__crate__api__simple__init_app =
|
||||
_wire__crate__api__simple__init_appPtr.asFunction<void Function(int)>();
|
||||
|
||||
void wire__crate__api__image_processing__process_image_ml_from_data(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose> rgba_data,
|
||||
int width,
|
||||
int height,
|
||||
) {
|
||||
return _wire__crate__api__image_processing__process_image_ml_from_data(
|
||||
port_,
|
||||
rgba_data,
|
||||
width,
|
||||
height,
|
||||
);
|
||||
}
|
||||
|
||||
late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDBPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>(
|
||||
'frbgen_photos_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB');
|
||||
late final _rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB =
|
||||
_rust_arc_increment_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDBPtr
|
||||
.asFunction<void Function(ffi.Pointer<ffi.Void>)>();
|
||||
late final _wire__crate__api__image_processing__process_image_ml_from_dataPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose>,
|
||||
ffi.Uint32,
|
||||
ffi.Uint32,
|
||||
)>>(
|
||||
'frbgen_photos_wire__crate__api__image_processing__process_image_ml_from_data',
|
||||
);
|
||||
late final _wire__crate__api__image_processing__process_image_ml_from_data =
|
||||
_wire__crate__api__image_processing__process_image_ml_from_dataPtr
|
||||
.asFunction<
|
||||
void Function(
|
||||
int,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose>,
|
||||
int,
|
||||
int,
|
||||
)>();
|
||||
|
||||
void
|
||||
rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB(
|
||||
ffi.Pointer<ffi.Void> ptr,
|
||||
void wire__crate__api__image_processing__process_image_ml_from_path(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> image_path,
|
||||
) {
|
||||
return _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB(
|
||||
ptr,
|
||||
return _wire__crate__api__image_processing__process_image_ml_from_path(
|
||||
port_,
|
||||
image_path,
|
||||
);
|
||||
}
|
||||
|
||||
late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDBPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>(
|
||||
'frbgen_photos_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB');
|
||||
late final _rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDB =
|
||||
_rust_arc_decrement_strong_count_RustOpaque_flutter_rust_bridgefor_generatedRustAutoOpaqueInnerVectorDBPtr
|
||||
.asFunction<void Function(ffi.Pointer<ffi.Void>)>();
|
||||
late final _wire__crate__api__image_processing__process_image_ml_from_pathPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)>>(
|
||||
'frbgen_photos_wire__crate__api__image_processing__process_image_ml_from_path',
|
||||
);
|
||||
late final _wire__crate__api__image_processing__process_image_ml_from_path =
|
||||
_wire__crate__api__image_processing__process_image_ml_from_pathPtr
|
||||
.asFunction<
|
||||
void Function(int, ffi.Pointer<wire_cst_list_prim_u_8_strict>)>();
|
||||
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose> cst_new_list_prim_u_8_loose(
|
||||
int len,
|
||||
) {
|
||||
return _cst_new_list_prim_u_8_loose(len);
|
||||
}
|
||||
|
||||
late final _cst_new_list_prim_u_8_loosePtr = _lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_loose> Function(
|
||||
ffi.Int32)>>('frbgen_photos_cst_new_list_prim_u_8_loose');
|
||||
late final _cst_new_list_prim_u_8_loose = _cst_new_list_prim_u_8_loosePtr
|
||||
.asFunction<ffi.Pointer<wire_cst_list_prim_u_8_loose> Function(int)>();
|
||||
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> cst_new_list_prim_u_8_strict(
|
||||
int len,
|
||||
) {
|
||||
return _cst_new_list_prim_u_8_strict(len);
|
||||
}
|
||||
|
||||
late final _cst_new_list_prim_u_8_strictPtr = _lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> Function(
|
||||
ffi.Int32)>>('frbgen_photos_cst_new_list_prim_u_8_strict');
|
||||
late final _cst_new_list_prim_u_8_strict = _cst_new_list_prim_u_8_strictPtr
|
||||
.asFunction<ffi.Pointer<wire_cst_list_prim_u_8_strict> Function(int)>();
|
||||
|
||||
int dummy_method_to_enforce_bundling() {
|
||||
return _dummy_method_to_enforce_bundling();
|
||||
}
|
||||
|
||||
late final _dummy_method_to_enforce_bundlingPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Int64 Function()>>(
|
||||
'dummy_method_to_enforce_bundling',
|
||||
);
|
||||
late final _dummy_method_to_enforce_bundling =
|
||||
_dummy_method_to_enforce_bundlingPtr.asFunction<int Function()>();
|
||||
}
|
||||
|
||||
typedef DartPostCObjectFnType
|
||||
= ffi.Pointer<ffi.NativeFunction<DartPostCObjectFnTypeFunction>>;
|
||||
typedef DartPostCObjectFnTypeFunction = ffi.Bool Function(
|
||||
DartPort port_id, ffi.Pointer<ffi.Void> message);
|
||||
typedef DartDartPostCObjectFnTypeFunction = bool Function(
|
||||
DartDartPort port_id, ffi.Pointer<ffi.Void> message);
|
||||
typedef DartPort = ffi.Int64;
|
||||
typedef DartDartPort = int;
|
||||
|
||||
final class wire_cst_list_prim_u_8_strict extends ffi.Struct {
|
||||
external ffi.Pointer<ffi.Uint8> ptr;
|
||||
|
||||
@ffi.Int32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_list_prim_u_8_loose extends ffi.Struct {
|
||||
external ffi.Pointer<ffi.Uint8> ptr;
|
||||
|
||||
@ffi.Int32()
|
||||
external int len;
|
||||
}
|
||||
|
||||
final class wire_cst_record_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize_list_prim_u_8_strict_usize_usize
|
||||
extends ffi.Struct {
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> field0;
|
||||
|
||||
@ffi.UintPtr()
|
||||
external int field1;
|
||||
|
||||
@ffi.UintPtr()
|
||||
external int field2;
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> field3;
|
||||
|
||||
@ffi.UintPtr()
|
||||
external int field4;
|
||||
|
||||
@ffi.UintPtr()
|
||||
external int field5;
|
||||
|
||||
external ffi.Pointer<wire_cst_list_prim_u_8_strict> field6;
|
||||
|
||||
@ffi.UintPtr()
|
||||
external int field7;
|
||||
|
||||
@ffi.UintPtr()
|
||||
external int field8;
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ class ExtentsPageView extends StatefulWidget {
|
||||
required this.childrenDelegate,
|
||||
this.dragStartBehavior = DragStartBehavior.start,
|
||||
this.openDrawer,
|
||||
}) : extents = 0;
|
||||
}) : extents = 0;
|
||||
|
||||
/// The number of pages to build off screen.
|
||||
///
|
||||
|
||||
@@ -9,7 +9,8 @@ import "package:photos/utils/navigation_util.dart";
|
||||
class HeaderErrorWidget extends StatelessWidget {
|
||||
final Error? _error;
|
||||
|
||||
const HeaderErrorWidget({super.key, required Error? error}) : _error = error;
|
||||
const HeaderErrorWidget({super.key, required Error? error})
|
||||
: _error = error;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
import "dart:async";
|
||||
import "dart:math" show Random;
|
||||
import "dart:typed_data" show Float32List;
|
||||
|
||||
import "package:flutter/foundation.dart" show kDebugMode;
|
||||
import 'package:flutter/material.dart';
|
||||
import "package:flutter_rust_bridge/flutter_rust_bridge.dart";
|
||||
import "package:logging/logging.dart";
|
||||
import "package:ml_linalg/linalg.dart";
|
||||
import "package:path_provider/path_provider.dart";
|
||||
import "package:photos/core/constants.dart";
|
||||
import "package:photos/core/event_bus.dart";
|
||||
import "package:photos/db/ml/clip_vector_db.dart";
|
||||
import "package:photos/db/ml/db.dart";
|
||||
import "package:photos/events/people_changed_event.dart";
|
||||
import "package:photos/extensions/stop_watch.dart";
|
||||
import "package:photos/generated/protos/ente/common/vector.pb.dart";
|
||||
import "package:photos/models/ml/face/person.dart";
|
||||
import "package:photos/models/search/generic_search_result.dart";
|
||||
import "package:photos/service_locator.dart";
|
||||
@@ -24,8 +15,6 @@ import 'package:photos/services/machine_learning/ml_service.dart';
|
||||
import "package:photos/services/machine_learning/semantic_search/semantic_search_service.dart";
|
||||
import "package:photos/services/notification_service.dart";
|
||||
import "package:photos/services/search_service.dart";
|
||||
import "package:photos/src/rust/api/simple.dart";
|
||||
import "package:photos/src/rust/api/usearch_api.dart";
|
||||
import 'package:photos/theme/ente_theme.dart';
|
||||
import 'package:photos/ui/components/captioned_text_widget.dart';
|
||||
import 'package:photos/ui/components/expandable_menu_item_widget.dart';
|
||||
@@ -195,380 +184,6 @@ class _MLDebugSectionWidgetState extends State<MLDebugSectionWidget> {
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Do some basic usearch",
|
||||
),
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async {
|
||||
try {
|
||||
// randomly generate some vectors and keys
|
||||
final random = Random();
|
||||
final tenEmbeddings = List.generate(
|
||||
10,
|
||||
(index) {
|
||||
final randomList = List<double>.generate(
|
||||
192,
|
||||
(_) => random.nextDouble(), // Values between 0 and 1
|
||||
);
|
||||
final randomVector = Vector.fromList(randomList).normalize();
|
||||
return Float32List.fromList(randomVector.toList());
|
||||
},
|
||||
);
|
||||
final tenKeys = Uint64List.fromList(
|
||||
List.generate(
|
||||
tenEmbeddings.length,
|
||||
(index) => BigInt.from(index + 1),
|
||||
),
|
||||
);
|
||||
final embedDimensions = BigInt.from(tenEmbeddings.first.length);
|
||||
final indexPath = (await getApplicationSupportDirectory()).path +
|
||||
"/ml/test/vector_db_index.usearch";
|
||||
final rustVectorDB = VectorDb(
|
||||
filePath: indexPath,
|
||||
dimensions: embedDimensions,
|
||||
);
|
||||
await rustVectorDB.resetIndex();
|
||||
final stats = await rustVectorDB.getIndexStats();
|
||||
logger.info("vector_db stats: $stats");
|
||||
await rustVectorDB.bulkAddVectors(
|
||||
keys: tenKeys,
|
||||
vectors: tenEmbeddings,
|
||||
);
|
||||
final statsAgain = await rustVectorDB.getIndexStats();
|
||||
logger.info("vector_db stats again: $statsAgain");
|
||||
final size = statsAgain.$1;
|
||||
final capacity = statsAgain.$2;
|
||||
final dimensions = statsAgain.$3;
|
||||
showShortToast(
|
||||
context,
|
||||
"Size: $size, Capacity: $capacity, Dimensions: $dimensions",
|
||||
);
|
||||
await rustVectorDB.deleteIndex();
|
||||
} catch (e, s) {
|
||||
logger.warning('Rust bridge failed ', e, s);
|
||||
await showGenericErrorDialog(context: context, error: e);
|
||||
}
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Fill ClipVectorDB",
|
||||
),
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async {
|
||||
try {
|
||||
final allClip = await MLDataDB.instance.getAllClipVectors();
|
||||
final allClipSmall = allClip.sublist(0, 15000);
|
||||
showShortToast(context, "Got all embeddings");
|
||||
logger.info("Got all embeddings");
|
||||
|
||||
final clipVectorDB = ClipVectorDB.instance;
|
||||
await clipVectorDB.deleteAllEmbeddings();
|
||||
logger.info("Clean vector DB");
|
||||
final stats = await clipVectorDB.getIndexStats();
|
||||
logger.info(stats.toString());
|
||||
showShortToast(context, stats.toString());
|
||||
|
||||
final fileIDs = allClipSmall.map((e) => e.fileID).toList();
|
||||
final embeddings = allClipSmall
|
||||
.map((e) => Float32List.fromList(e.vector.toList()))
|
||||
.toList();
|
||||
showShortToast(context, "Reshaped embeddings data");
|
||||
logger.info("Reshaped embeddings data");
|
||||
|
||||
final now = DateTime.now();
|
||||
await clipVectorDB.bulkInsertEmbeddings(
|
||||
fileIDs: fileIDs,
|
||||
embeddings: embeddings,
|
||||
);
|
||||
final duration = DateTime.now().difference(now);
|
||||
logger.info(
|
||||
"ClipVectorDB bulk insert took ${duration.inMilliseconds} ms for ${fileIDs.length} embeddings",
|
||||
);
|
||||
final statsAfter = await clipVectorDB.getIndexStats();
|
||||
logger.info(statsAfter.toString());
|
||||
showShortToast(context, statsAfter.toString());
|
||||
} catch (e, s) {
|
||||
logger.warning('ClipVectorDB migration failed ', e, s);
|
||||
await showGenericErrorDialog(context: context, error: e);
|
||||
}
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Migrate to ClipVectorDB",
|
||||
),
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async {
|
||||
try {
|
||||
await MLDataDB.instance.checkMigrateFillClipVectorDB();
|
||||
showShortToast(context, "Migration done!");
|
||||
} catch (e, s) {
|
||||
logger.warning('ClipVectorDB migration failed ', e, s);
|
||||
await showGenericErrorDialog(context: context, error: e);
|
||||
}
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Show ClipVectorDB stats",
|
||||
),
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async {
|
||||
try {
|
||||
final clipVectorDB = ClipVectorDB.instance;
|
||||
final stats = await clipVectorDB.getIndexStats();
|
||||
logger.info(stats.toString());
|
||||
showShortToast(context, stats.toString());
|
||||
} catch (e, s) {
|
||||
logger.warning('ClipVectorDB stats failed ', e, s);
|
||||
await showGenericErrorDialog(context: context, error: e);
|
||||
}
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Delete/Empty ClipVectorDB",
|
||||
),
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async {
|
||||
try {
|
||||
final clipVectorDB = ClipVectorDB.instance;
|
||||
await clipVectorDB.deleteIndex();
|
||||
} catch (e, s) {
|
||||
logger.warning('ClipVectorDB cleanup failed ', e, s);
|
||||
await showGenericErrorDialog(context: context, error: e);
|
||||
}
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Benchmark Vector DB Face",
|
||||
),
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async {
|
||||
try {
|
||||
final w = (kDebugMode ? EnteWatch('MLDebugSectionWidget') : null)
|
||||
?..start();
|
||||
final persons = await PersonService.instance.getPersons();
|
||||
w?.log('get all persons for ${persons.length} persons');
|
||||
String laurensID = '';
|
||||
for (final person in persons) {
|
||||
if (person.data.name.toLowerCase().contains('laurens')) {
|
||||
laurensID = person.remoteID;
|
||||
}
|
||||
}
|
||||
if (laurensID.isEmpty) {
|
||||
throw Exception('Laurens not found');
|
||||
}
|
||||
final laurensFaceIDs =
|
||||
await MLDataDB.instance.getFaceIDsForPerson(laurensID);
|
||||
w?.log(
|
||||
'getting all face ids for laurens (${laurensFaceIDs.length} faces)',
|
||||
);
|
||||
final laurensFaceIdToEmbeddingData = await MLDataDB.instance
|
||||
.getFaceEmbeddingMapForFaces(laurensFaceIDs);
|
||||
|
||||
// Fill the vector DB with all embeddings
|
||||
final laurensFaceIdToFloat32 = laurensFaceIdToEmbeddingData.map(
|
||||
(key, value) => MapEntry(
|
||||
key,
|
||||
Float32List.fromList(EVector.fromBuffer(value).values),
|
||||
),
|
||||
);
|
||||
final keys = Uint64List.fromList(
|
||||
List.generate(
|
||||
laurensFaceIdToFloat32.length,
|
||||
(index) => BigInt.from(index + 1),
|
||||
),
|
||||
);
|
||||
final vectorDB = VectorDb(
|
||||
filePath: (await getApplicationSupportDirectory()).path +
|
||||
"/ml/test/vector_db_face_index.usearch",
|
||||
dimensions: BigInt.from(
|
||||
laurensFaceIdToFloat32.values.first.length,
|
||||
),
|
||||
);
|
||||
await vectorDB.resetIndex();
|
||||
await vectorDB.bulkAddVectors(
|
||||
keys: keys,
|
||||
vectors: laurensFaceIdToFloat32.values.toList(),
|
||||
);
|
||||
|
||||
// Benchmarking the vector DB
|
||||
final queries = laurensFaceIdToFloat32.values.toList();
|
||||
final count = BigInt.from(10);
|
||||
w?.reset();
|
||||
final (vectorKeys, distances) = await vectorDB.bulkSearchVectors(
|
||||
queries: queries,
|
||||
count: count,
|
||||
);
|
||||
|
||||
w?.log(
|
||||
'Done with ${queries.length * queries.length} (${queries.length} x ${queries.length}}) embeddings comparisons in vector DB',
|
||||
);
|
||||
logger.info(
|
||||
'vector db results: ${vectorKeys.length} results, first: ${vectorKeys.first}, hundredth: ${vectorKeys[99]}',
|
||||
);
|
||||
|
||||
// Benchmarking our own vector comparisons
|
||||
final laurensFaceIdToEmbeddingVectors =
|
||||
laurensFaceIdToEmbeddingData.map(
|
||||
(key, value) => MapEntry(
|
||||
key,
|
||||
Vector.fromList(EVector.fromBuffer(value).values),
|
||||
),
|
||||
);
|
||||
final faceVectors = laurensFaceIdToEmbeddingVectors.values;
|
||||
w?.reset();
|
||||
for (final faceVector in faceVectors) {
|
||||
for (final otherFaceVector in faceVectors) {
|
||||
final _ = 1 - faceVector.dot(otherFaceVector);
|
||||
}
|
||||
}
|
||||
|
||||
w?.log(
|
||||
'Done with ${faceVectors.length * faceVectors.length} (${faceVectors.length} x ${faceVectors.length}}) embeddings comparisons in own method',
|
||||
);
|
||||
await vectorDB.deleteIndex();
|
||||
} catch (e, s) {
|
||||
logger.warning('vector DB search failed ', e, s);
|
||||
|
||||
await showGenericErrorDialog(context: context, error: e);
|
||||
}
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Benchmark Vector DB CLIP",
|
||||
),
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async {
|
||||
try {
|
||||
final w = (kDebugMode ? EnteWatch('MLDebugSectionWidget') : null)
|
||||
?..start();
|
||||
final clipEmbeddings = await mlDataDB.getAllClipVectors();
|
||||
w?.log(
|
||||
'getting all clip embeddings (${clipEmbeddings.length} embeddings)',
|
||||
);
|
||||
|
||||
// Fill the vector DB with all embeddings
|
||||
final clipFloat32 = clipEmbeddings
|
||||
.map(
|
||||
(value) => Float32List.fromList(value.vector.toList()),
|
||||
)
|
||||
.toList();
|
||||
final keys = Uint64List.fromList(
|
||||
List.generate(
|
||||
clipFloat32.length,
|
||||
(index) => BigInt.from(index + 1),
|
||||
),
|
||||
);
|
||||
final vectorDB = VectorDb(
|
||||
filePath: (await getApplicationSupportDirectory()).path +
|
||||
"/ml/test/vector_db_clip_index.usearch",
|
||||
dimensions: BigInt.from(
|
||||
clipFloat32.first.length,
|
||||
),
|
||||
);
|
||||
await vectorDB.resetIndex();
|
||||
await vectorDB.bulkAddVectors(
|
||||
keys: keys,
|
||||
vectors: clipFloat32,
|
||||
);
|
||||
|
||||
// Benchmarking the vector DB
|
||||
final count = BigInt.from(10);
|
||||
w?.reset();
|
||||
final (vectorKeys, distances) = await vectorDB.bulkSearchVectors(
|
||||
queries: clipFloat32,
|
||||
count: count,
|
||||
);
|
||||
|
||||
w?.log(
|
||||
'Done with ${clipFloat32.length * clipFloat32.length} (${clipFloat32.length} x ${clipFloat32.length}}) embeddings comparisons in vector DB',
|
||||
);
|
||||
logger.info(
|
||||
'vector db results: ${vectorKeys.length} results, first: ${vectorKeys.first} with distances ${distances.first}, hundredth: ${vectorKeys[99]} with distances ${distances[99]}',
|
||||
);
|
||||
|
||||
// // Benchmarking our own vector comparisons
|
||||
// final clipVectors = clipEmbeddings
|
||||
// .map(
|
||||
// (value) => value.vector,
|
||||
// )
|
||||
// .toList();
|
||||
// w?.reset();
|
||||
// int compared = 0;
|
||||
// int ms = DateTime.now().millisecondsSinceEpoch;
|
||||
// for (final faceVector in clipVectors) {
|
||||
// for (final otherFaceVector in clipVectors) {
|
||||
// final _ = 1 - faceVector.dot(otherFaceVector);
|
||||
// }
|
||||
// compared++;
|
||||
// if (compared % 100 == 0) {
|
||||
// final now = DateTime.now().millisecondsSinceEpoch;
|
||||
// logger.info(
|
||||
// 'Compared next 100 in ${now - ms} ms, progress: ($compared / ${clipVectors.length})',
|
||||
// );
|
||||
// ms = now;
|
||||
// }
|
||||
// }
|
||||
// w?.log(
|
||||
// 'Done with ${clipVectors.length * clipVectors.length} (${clipVectors.length} x ${clipVectors.length}}) embeddings comparisons in own method',
|
||||
// );
|
||||
|
||||
await vectorDB.deleteIndex();
|
||||
} catch (e, s) {
|
||||
logger.warning('vector DB search failed ', e, s);
|
||||
|
||||
await showGenericErrorDialog(context: context, error: e);
|
||||
}
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Test rust bridge",
|
||||
),
|
||||
pressedColor: getEnteColorScheme(context).fillFaint,
|
||||
trailingIcon: Icons.chevron_right_outlined,
|
||||
trailingIconIsMuted: true,
|
||||
onTap: () async {
|
||||
try {
|
||||
final String greetings = greet(name: "Tom");
|
||||
const String expected = "Hello, Tom!";
|
||||
assert(greetings == expected);
|
||||
debugPrint("String from rust: $greetings");
|
||||
showShortToast(context, greetings);
|
||||
} catch (e, s) {
|
||||
logger.warning('Rust bridge failed ', e, s);
|
||||
await showGenericErrorDialog(context: context, error: e);
|
||||
}
|
||||
},
|
||||
),
|
||||
sectionOptionSpacing,
|
||||
MenuItemWidget(
|
||||
captionedTextWidget: FutureBuilder<IndexStatus>(
|
||||
future: getIndexStatus(),
|
||||
|
||||
@@ -57,69 +57,57 @@ class _MLUserDeveloperOptionsState extends State<MLUserDeveloperOptions> {
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 48),
|
||||
widget.mlIsEnabled
|
||||
? ButtonWidget(
|
||||
buttonType: ButtonType.neutral,
|
||||
labelText: "Purge empty indices",
|
||||
onTap: () async {
|
||||
await deleteEmptyIndices(context);
|
||||
},
|
||||
)
|
||||
: const SizedBox(),
|
||||
widget.mlIsEnabled
|
||||
? const SizedBox(height: 24)
|
||||
: const SizedBox(),
|
||||
widget.mlIsEnabled
|
||||
? ButtonWidget(
|
||||
buttonType: ButtonType.neutral,
|
||||
labelText: "Reset all local ML",
|
||||
onTap: () async {
|
||||
await deleteAllLocalML(context);
|
||||
},
|
||||
)
|
||||
: const SizedBox(),
|
||||
widget.mlIsEnabled
|
||||
? const SizedBox(height: 24)
|
||||
: const SizedBox(),
|
||||
widget.mlIsEnabled
|
||||
? MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Remote fetch",
|
||||
),
|
||||
menuItemColor: colorScheme.fillFaint,
|
||||
trailingWidget: ToggleSwitchWidget(
|
||||
value: () => localSettings.remoteFetchEnabled,
|
||||
onChanged: () async {
|
||||
try {
|
||||
await localSettings.toggleRemoteFetch();
|
||||
_logger.info(
|
||||
'Remote fetch is turned ${localSettings.remoteFetchEnabled ? 'on' : 'off'}',
|
||||
);
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
} catch (e, s) {
|
||||
_logger.warning(
|
||||
'Remote fetch toggle failed ',
|
||||
e,
|
||||
s,
|
||||
);
|
||||
await showGenericErrorDialog(
|
||||
context: context,
|
||||
error: e,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
singleBorderRadius: 8,
|
||||
alignCaptionedTextToLeft: true,
|
||||
isBottomBorderRadiusRemoved: true,
|
||||
isGestureDetectorDisabled: true,
|
||||
)
|
||||
: const SizedBox(),
|
||||
widget.mlIsEnabled
|
||||
? const SizedBox(height: 24)
|
||||
: const SizedBox.shrink(),
|
||||
widget.mlIsEnabled ? ButtonWidget(
|
||||
buttonType: ButtonType.neutral,
|
||||
labelText: "Purge empty indices",
|
||||
onTap: () async {
|
||||
await deleteEmptyIndices(context);
|
||||
},
|
||||
) : const SizedBox(),
|
||||
widget.mlIsEnabled ? const SizedBox(height: 24) : const SizedBox(),
|
||||
widget.mlIsEnabled ? ButtonWidget(
|
||||
buttonType: ButtonType.neutral,
|
||||
labelText: "Reset all local ML",
|
||||
onTap: () async {
|
||||
await deleteAllLocalML(context);
|
||||
},
|
||||
) : const SizedBox(),
|
||||
widget.mlIsEnabled ? const SizedBox(height: 24) : const SizedBox(),
|
||||
widget.mlIsEnabled ? MenuItemWidget(
|
||||
captionedTextWidget: const CaptionedTextWidget(
|
||||
title: "Remote fetch",
|
||||
),
|
||||
menuItemColor: colorScheme.fillFaint,
|
||||
trailingWidget: ToggleSwitchWidget(
|
||||
value: () => localSettings.remoteFetchEnabled,
|
||||
onChanged: () async {
|
||||
try {
|
||||
await localSettings.toggleRemoteFetch();
|
||||
_logger.info(
|
||||
'Remote fetch is turned ${localSettings.remoteFetchEnabled ? 'on' : 'off'}',
|
||||
);
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
} catch (e, s) {
|
||||
_logger.warning(
|
||||
'Remote fetch toggle failed ',
|
||||
e,
|
||||
s,
|
||||
);
|
||||
await showGenericErrorDialog(
|
||||
context: context,
|
||||
error: e,
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
singleBorderRadius: 8,
|
||||
alignCaptionedTextToLeft: true,
|
||||
isBottomBorderRadiusRemoved: true,
|
||||
isGestureDetectorDisabled: true,
|
||||
) : const SizedBox(),
|
||||
widget.mlIsEnabled ? const SizedBox(height: 24) : const SizedBox.shrink(),
|
||||
ButtonWidget(
|
||||
buttonType: ButtonType.neutral,
|
||||
labelText: "Load face detection model",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import "dart:async";
|
||||
|
||||
import 'package:fast_base58/fast_base58.dart';
|
||||
import "package:flutter/cupertino.dart";
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import "package:local_auth/local_auth.dart";
|
||||
|
||||
@@ -3,8 +3,7 @@ import "package:flutter/material.dart";
|
||||
import "package:photos/generated/l10n.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
|
||||
Future<DateTime?> showDatePickerSheet(
|
||||
BuildContext context, {
|
||||
Future<DateTime?> showDatePickerSheet(BuildContext context, {
|
||||
required DateTime initialDate,
|
||||
DateTime? maxDate,
|
||||
DateTime? minDate,
|
||||
|
||||
@@ -5,7 +5,6 @@ import "package:photos/theme/ente_theme.dart";
|
||||
import "package:photos/ui/components/info_item_widget.dart";
|
||||
import "package:photos/ui/viewer/date/edit_date_sheet.dart";
|
||||
import "package:photos/utils/standalone/date_time.dart";
|
||||
|
||||
class CreationTimeItem extends StatefulWidget {
|
||||
final EnteFile file;
|
||||
final int currentUserID;
|
||||
|
||||
@@ -54,7 +54,7 @@ class PickPersonCoverPhotoWidget extends StatelessWidget {
|
||||
final result = await SearchService.instance
|
||||
.getClusterFilesForPersonID(personEntity.remoteID);
|
||||
|
||||
final List<EnteFile> resultFiles = [];
|
||||
final resultFiles = <EnteFile>{};
|
||||
for (final e in result.entries) {
|
||||
resultFiles.addAll(e.value);
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ class _PeoplePageState extends State<PeoplePage> {
|
||||
);
|
||||
return [];
|
||||
}
|
||||
final List<EnteFile> resultFiles = [];
|
||||
final Set<EnteFile> resultFiles = {};
|
||||
for (final e in result.entries) {
|
||||
resultFiles.addAll(e.value);
|
||||
}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
|
||||
|
||||
import "package:photos/core/configuration.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/person/person_service.dart";
|
||||
|
||||
Future<bool> isMeAssigned() async {
|
||||
final personEntities = await PersonService.instance.getPersons();
|
||||
final currentUserEmail = Configuration.instance.getEmail();
|
||||
final personEntities = await PersonService.instance.getPersons();
|
||||
final currentUserEmail = Configuration.instance.getEmail();
|
||||
|
||||
bool isAssigned = false;
|
||||
for (final personEntity in personEntities) {
|
||||
if (personEntity.data.email == currentUserEmail) {
|
||||
isAssigned = true;
|
||||
break;
|
||||
bool isAssigned = false;
|
||||
for (final personEntity in personEntities) {
|
||||
if (personEntity.data.email == currentUserEmail) {
|
||||
isAssigned = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return isAssigned;
|
||||
}
|
||||
return isAssigned;
|
||||
}
|
||||
@@ -21,6 +21,7 @@ class PersonFaceWidget extends StatefulWidget {
|
||||
final String? clusterID;
|
||||
final bool useFullFile;
|
||||
final VoidCallback? onErrorCallback;
|
||||
final bool keepAlive;
|
||||
|
||||
// PersonFaceWidget constructor checks that both personId and clusterID are not null
|
||||
// and that the file is not null
|
||||
@@ -29,6 +30,7 @@ class PersonFaceWidget extends StatefulWidget {
|
||||
this.clusterID,
|
||||
this.useFullFile = true,
|
||||
this.onErrorCallback,
|
||||
this.keepAlive = false,
|
||||
super.key,
|
||||
}) : assert(
|
||||
personId != null || clusterID != null,
|
||||
@@ -39,12 +41,16 @@ class PersonFaceWidget extends StatefulWidget {
|
||||
State<PersonFaceWidget> createState() => _PersonFaceWidgetState();
|
||||
}
|
||||
|
||||
class _PersonFaceWidgetState extends State<PersonFaceWidget> {
|
||||
class _PersonFaceWidgetState extends State<PersonFaceWidget>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
Future<Uint8List?>? faceCropFuture;
|
||||
EnteFile? fileForFaceCrop;
|
||||
|
||||
bool get isPerson => widget.personId != null;
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => widget.keepAlive;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -64,6 +70,10 @@ class _PersonFaceWidgetState extends State<PersonFaceWidget> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(
|
||||
context,
|
||||
); // Calling super.build for AutomaticKeepAliveClientMixin
|
||||
|
||||
return FutureBuilder<Uint8List?>(
|
||||
future: faceCropFuture,
|
||||
builder: (context, snapshot) {
|
||||
@@ -163,7 +173,7 @@ class _PersonFaceWidgetState extends State<PersonFaceWidget> {
|
||||
}
|
||||
}
|
||||
if (fileForFaceCrop == null) {
|
||||
_logger.warning(
|
||||
_logger.severe(
|
||||
"No suitable file found for face crop for person: ${widget.personId} or cluster: ${widget.clusterID}",
|
||||
);
|
||||
return null;
|
||||
@@ -176,7 +186,7 @@ class _PersonFaceWidgetState extends State<PersonFaceWidget> {
|
||||
clusterID: widget.clusterID,
|
||||
);
|
||||
if (face == null) {
|
||||
debugPrint(
|
||||
_logger.severe(
|
||||
"No cover face for person: ${widget.personId} or cluster ${widget.clusterID} and fileID ${fileForFaceCrop.uploadedFileID!}",
|
||||
);
|
||||
return null;
|
||||
@@ -188,7 +198,13 @@ class _PersonFaceWidgetState extends State<PersonFaceWidget> {
|
||||
personOrClusterID: personOrClusterId,
|
||||
useTempCache: false,
|
||||
);
|
||||
return cropMap?[face.faceID];
|
||||
final result = cropMap?[face.faceID];
|
||||
if (result == null) {
|
||||
_logger.severe(
|
||||
"Null cover face crop for person: ${widget.personId} or cluster ${widget.clusterID} and fileID ${fileForFaceCrop.uploadedFileID!}",
|
||||
);
|
||||
}
|
||||
return result;
|
||||
} catch (e, s) {
|
||||
_logger.severe(
|
||||
"Error getting cover face for person: ${widget.personId} or cluster ${widget.clusterID}",
|
||||
|
||||
@@ -95,12 +95,14 @@ class SelectablePersonSearchExample extends StatelessWidget {
|
||||
final GenericSearchResult searchResult;
|
||||
final double size;
|
||||
final SelectedPeople selectedPeople;
|
||||
final bool isDefaultFace;
|
||||
|
||||
const SelectablePersonSearchExample({
|
||||
super.key,
|
||||
required this.searchResult,
|
||||
required this.selectedPeople,
|
||||
this.size = 102,
|
||||
this.isDefaultFace = false,
|
||||
});
|
||||
|
||||
void _handleTap(BuildContext context) {
|
||||
@@ -192,7 +194,10 @@ class SelectablePersonSearchExample extends StatelessWidget {
|
||||
searchResult.previewThumbnail()!,
|
||||
shouldShowSyncStatus: false,
|
||||
)
|
||||
: FaceSearchResult(searchResult);
|
||||
: FaceSearchResult(
|
||||
searchResult,
|
||||
isDefaultFace: isDefaultFace,
|
||||
);
|
||||
} else {
|
||||
child = const NoThumbnailWidget(
|
||||
addBorder: false,
|
||||
@@ -301,8 +306,13 @@ class SelectablePersonSearchExample extends StatelessWidget {
|
||||
|
||||
class FaceSearchResult extends StatelessWidget {
|
||||
final SearchResult searchResult;
|
||||
final bool isDefaultFace;
|
||||
|
||||
const FaceSearchResult(this.searchResult, {super.key});
|
||||
const FaceSearchResult(
|
||||
this.searchResult, {
|
||||
super.key,
|
||||
this.isDefaultFace = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -313,6 +323,7 @@ class FaceSearchResult extends StatelessWidget {
|
||||
key: params.containsKey(kPersonWidgetKey)
|
||||
? ValueKey(params[kPersonWidgetKey])
|
||||
: ValueKey(params[kPersonParamID] ?? params[kClusterParamId]),
|
||||
keepAlive: isDefaultFace,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -486,6 +497,7 @@ class _PeopleSectionAllWidgetState extends State<PeopleSectionAllWidget> {
|
||||
searchResult: normalFaces[index],
|
||||
size: itemSize,
|
||||
selectedPeople: widget.selectedPeople!,
|
||||
isDefaultFace: true,
|
||||
)
|
||||
: PersonSearchExample(
|
||||
searchResult: normalFaces[index],
|
||||
@@ -525,6 +537,7 @@ class _PeopleSectionAllWidgetState extends State<PeopleSectionAllWidget> {
|
||||
searchResult: extraFaces[index],
|
||||
size: itemSize,
|
||||
selectedPeople: widget.selectedPeople!,
|
||||
isDefaultFace: false,
|
||||
)
|
||||
: PersonSearchExample(
|
||||
searchResult: extraFaces[index],
|
||||
|
||||
@@ -136,7 +136,7 @@ Future<Map<String, Uint8List>?> getCachedFaceCrops(
|
||||
facesWithoutCrops[face.faceID] = face.detection.box;
|
||||
}
|
||||
} catch (e, s) {
|
||||
_logger.severe(
|
||||
_logger.warning(
|
||||
"Error reading cached face crop for faceID ${face.faceID} from file ${faceCropCacheFile.path}",
|
||||
e,
|
||||
s,
|
||||
@@ -212,7 +212,7 @@ Future<Map<String, Uint8List>?> getCachedFaceCrops(
|
||||
milliseconds: 100 * pow(2, fetchAttempt + 1).toInt(),
|
||||
);
|
||||
await Future.delayed(backoff);
|
||||
_logger.warning(
|
||||
_logger.fine(
|
||||
"Error getting face crops for faceIDs: ${faces.map((face) => face.faceID).toList()}, retrying (attempt ${fetchAttempt + 1}) in ${backoff.inMilliseconds} ms",
|
||||
e,
|
||||
s,
|
||||
@@ -225,13 +225,13 @@ Future<Map<String, Uint8List>?> getCachedFaceCrops(
|
||||
useTempCache: useTempCache,
|
||||
);
|
||||
}
|
||||
_logger.severe(
|
||||
_logger.warning(
|
||||
"Error getting face crops for faceIDs: ${faces.map((face) => face.faceID).toList()}",
|
||||
e,
|
||||
s,
|
||||
);
|
||||
} else {
|
||||
_logger.info(
|
||||
_logger.severe(
|
||||
"Stopped getting face crops for faceIDs: ${faces.map((face) => face.faceID).toList()} due to $e",
|
||||
);
|
||||
}
|
||||
@@ -334,12 +334,14 @@ Future<Map<String, Uint8List>?> _getFaceCrops(
|
||||
if (useFullFile && file.fileType != FileType.video) {
|
||||
final File? ioFile = await getFile(file);
|
||||
if (ioFile == null) {
|
||||
_logger.severe("Failed to get file for face crop generation");
|
||||
return null;
|
||||
}
|
||||
imagePath = ioFile.path;
|
||||
} else {
|
||||
final thumbnail = await getThumbnailForUploadedFile(file);
|
||||
if (thumbnail == null) {
|
||||
_logger.severe("Failed to get thumbnail for face crop generation");
|
||||
return null;
|
||||
}
|
||||
imagePath = thumbnail.path;
|
||||
|
||||
@@ -23,7 +23,7 @@ import "package:photos/events/file_uploaded_event.dart";
|
||||
import 'package:photos/events/files_updated_event.dart';
|
||||
import 'package:photos/events/local_photos_updated_event.dart';
|
||||
import 'package:photos/events/subscription_purchased_event.dart';
|
||||
import "package:photos/main.dart";
|
||||
import 'package:photos/main.dart';
|
||||
import "package:photos/models/api/metadata.dart";
|
||||
import "package:photos/models/backup/backup_item.dart";
|
||||
import "package:photos/models/backup/backup_item_status.dart";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import "dart:async";
|
||||
import "dart:io" show File, Platform;
|
||||
import "dart:math" show exp, max, min, pi;
|
||||
import "dart:math" show max, min;
|
||||
import "dart:typed_data" show Float32List, Uint8List;
|
||||
import "dart:ui";
|
||||
|
||||
@@ -24,14 +24,23 @@ final _logger = Logger("ImageMlUtil");
|
||||
/// These are 8 bit unsigned integers in range 0-255 for each RGB channel
|
||||
typedef RGB = (int, int, int);
|
||||
|
||||
const gaussianKernelSize = 5;
|
||||
const gaussianKernelRadius = gaussianKernelSize ~/ 2;
|
||||
const gaussianSigma = 10.0;
|
||||
final List<List<double>> gaussianKernel =
|
||||
create2DGaussianKernel(gaussianKernelSize, gaussianSigma);
|
||||
|
||||
const maxKernelSize = gaussianKernelSize;
|
||||
const maxKernelRadius = maxKernelSize ~/ 2;
|
||||
const List<String> supportedRustImageFormats = [
|
||||
'bmp',
|
||||
'dds',
|
||||
'farbfeld',
|
||||
'gif',
|
||||
'hdr',
|
||||
'ico',
|
||||
'jpg',
|
||||
'jpeg',
|
||||
'exr',
|
||||
'png',
|
||||
'pnm',
|
||||
'qoi',
|
||||
'tga',
|
||||
'tiff',
|
||||
'webp',
|
||||
];
|
||||
|
||||
// Face thumbnail compression constants
|
||||
const int _faceThumbnailCompressionQuality = 90;
|
||||
@@ -102,6 +111,11 @@ Future<DecodedImage> decodeImageFromPath(
|
||||
return DecodedImage(image, rawRgbaBytes);
|
||||
}
|
||||
|
||||
bool canRustDecodeImage(String imagePath) {
|
||||
final format = imagePath.split('.').last;
|
||||
return supportedRustImageFormats.contains(format);
|
||||
}
|
||||
|
||||
/// Decodes [Uint8List] image data to an ui.[Image] object.
|
||||
Future<Image> decodeImageFromData(Uint8List imageData) async {
|
||||
// Decoding using flutter paint. This is the fastest and easiest method.
|
||||
@@ -218,18 +232,19 @@ Future<List<Uint8List>> generateFaceThumbnailsUsingCanvas(
|
||||
}
|
||||
}
|
||||
|
||||
Future<(Float32List, Dimensions)> preprocessImageYoloFace(
|
||||
Image image,
|
||||
Uint8List rawRgbaBytes,
|
||||
Future<Float32List> resizedToPreprocessedYoloFace(
|
||||
Uint8List rgbBytes,
|
||||
int rgbWidth,
|
||||
int rgbHeight,
|
||||
) async {
|
||||
const requiredWidth = 640;
|
||||
const requiredHeight = 640;
|
||||
final scale = min(requiredWidth / image.width, requiredHeight / image.height);
|
||||
final scaledWidth = (image.width * scale).round().clamp(0, requiredWidth);
|
||||
final scaledHeight = (image.height * scale).round().clamp(0, requiredHeight);
|
||||
final int letterboxWidth = requiredWidth - rgbWidth;
|
||||
final int letterboxHeight = requiredHeight - rgbHeight;
|
||||
final int letterboxWidthHalf = letterboxWidth ~/ 2;
|
||||
final int letterboxHeightHalf = letterboxHeight ~/ 2;
|
||||
|
||||
final processedBytes = Float32List(3 * requiredHeight * requiredWidth);
|
||||
|
||||
final buffer = Float32List.view(processedBytes.buffer);
|
||||
int pixelIndex = 0;
|
||||
const int channelOffsetGreen = requiredHeight * requiredWidth;
|
||||
@@ -237,14 +252,18 @@ Future<(Float32List, Dimensions)> preprocessImageYoloFace(
|
||||
for (var h = 0; h < requiredHeight; h++) {
|
||||
for (var w = 0; w < requiredWidth; w++) {
|
||||
late RGB pixel;
|
||||
if (w >= scaledWidth || h >= scaledHeight) {
|
||||
if (w < letterboxWidthHalf ||
|
||||
w >= rgbWidth + letterboxWidthHalf ||
|
||||
h < letterboxHeightHalf ||
|
||||
h >= rgbHeight + letterboxHeightHalf) {
|
||||
pixel = const (114, 114, 114);
|
||||
} else {
|
||||
pixel = _getPixelBilinear(
|
||||
w / scale,
|
||||
h / scale,
|
||||
image,
|
||||
rawRgbaBytes,
|
||||
final int byteIndex = 3 *
|
||||
(rgbWidth * (h - letterboxHeightHalf) + (w - letterboxWidthHalf));
|
||||
pixel = (
|
||||
rgbBytes[byteIndex],
|
||||
rgbBytes[byteIndex + 1],
|
||||
rgbBytes[byteIndex + 2]
|
||||
);
|
||||
}
|
||||
buffer[pixelIndex] = pixel.$1 / 255;
|
||||
@@ -254,40 +273,35 @@ Future<(Float32List, Dimensions)> preprocessImageYoloFace(
|
||||
}
|
||||
}
|
||||
|
||||
return (processedBytes, Dimensions(width: scaledWidth, height: scaledHeight));
|
||||
return processedBytes;
|
||||
}
|
||||
|
||||
Future<Float32List> preprocessImageClip(
|
||||
Image image,
|
||||
Uint8List rawRgbaBytes,
|
||||
Future<Float32List> resizedToPreprocessedClip(
|
||||
Uint8List rgbBytes,
|
||||
int rgbWidth,
|
||||
int rgbHeight,
|
||||
) async {
|
||||
const int requiredWidth = 256;
|
||||
const int requiredHeight = 256;
|
||||
const int requiredSize = 3 * requiredWidth * requiredHeight;
|
||||
final scale = max(requiredWidth / image.width, requiredHeight / image.height);
|
||||
final bool useAntiAlias = scale < 0.8;
|
||||
final scaledWidth = (image.width * scale).round();
|
||||
final scaledHeight = (image.height * scale).round();
|
||||
final widthOffset = max(0, scaledWidth - requiredWidth) / 2;
|
||||
final heightOffset = max(0, scaledHeight - requiredHeight) / 2;
|
||||
const requiredWidth = 256;
|
||||
const requiredHeight = 256;
|
||||
|
||||
final processedBytes = Float32List(requiredSize);
|
||||
final processedBytes = Float32List(3 * requiredHeight * requiredWidth);
|
||||
final buffer = Float32List.view(processedBytes.buffer);
|
||||
int pixelIndex = 0;
|
||||
const int greenOff = requiredHeight * requiredWidth;
|
||||
const int blueOff = 2 * requiredHeight * requiredWidth;
|
||||
for (var h = 0 + heightOffset; h < scaledHeight - heightOffset; h++) {
|
||||
for (var w = 0 + widthOffset; w < scaledWidth - widthOffset; w++) {
|
||||
final RGB pixel = _getPixelBilinear(
|
||||
w / scale,
|
||||
h / scale,
|
||||
image,
|
||||
rawRgbaBytes,
|
||||
antiAlias: useAntiAlias,
|
||||
const int channelOffsetGreen = requiredHeight * requiredWidth;
|
||||
const int channelOffsetBlue = 2 * requiredHeight * requiredWidth;
|
||||
final widthOffset = max(0, rgbWidth - requiredWidth) ~/ 2;
|
||||
final heightOffset = max(0, rgbHeight - requiredHeight) ~/ 2;
|
||||
for (var h = 0 + heightOffset; h < heightOffset + requiredHeight; h++) {
|
||||
for (var w = 0 + widthOffset; w < widthOffset + requiredWidth; w++) {
|
||||
final int byteIndex = 3 * (rgbWidth * h + w);
|
||||
final RGB pixel = (
|
||||
rgbBytes[byteIndex],
|
||||
rgbBytes[byteIndex + 1],
|
||||
rgbBytes[byteIndex + 2]
|
||||
);
|
||||
buffer[pixelIndex] = pixel.$1 / 255;
|
||||
buffer[pixelIndex + greenOff] = pixel.$2 / 255;
|
||||
buffer[pixelIndex + blueOff] = pixel.$3 / 255;
|
||||
buffer[pixelIndex + channelOffsetGreen] = pixel.$2 / 255;
|
||||
buffer[pixelIndex + channelOffsetBlue] = pixel.$3 / 255;
|
||||
pixelIndex++;
|
||||
}
|
||||
}
|
||||
@@ -297,20 +311,20 @@ Future<Float32List> preprocessImageClip(
|
||||
|
||||
Future<(Float32List, List<AlignmentResult>, List<bool>, List<double>, Size)>
|
||||
preprocessToMobileFaceNetFloat32List(
|
||||
Image image,
|
||||
Dimensions imageDimensions,
|
||||
Uint8List rawRgbaBytes,
|
||||
List<FaceDetectionRelative> relativeFaces, {
|
||||
int width = 112,
|
||||
int height = 112,
|
||||
}) async {
|
||||
final Size originalSize =
|
||||
Size(image.width.toDouble(), image.height.toDouble());
|
||||
Size(imageDimensions.width.toDouble(), imageDimensions.height.toDouble());
|
||||
|
||||
final List<FaceDetectionAbsolute> absoluteFaces =
|
||||
relativeToAbsoluteDetections(
|
||||
relativeDetections: relativeFaces,
|
||||
imageWidth: image.width,
|
||||
imageHeight: image.height,
|
||||
imageWidth: imageDimensions.width,
|
||||
imageHeight: imageDimensions.height,
|
||||
);
|
||||
|
||||
final alignedImagesFloat32List =
|
||||
@@ -334,7 +348,7 @@ Future<(Float32List, List<AlignmentResult>, List<bool>, List<double>, Size)>
|
||||
alignmentResults.add(alignmentResult);
|
||||
|
||||
_warpAffineFloat32List(
|
||||
image,
|
||||
imageDimensions,
|
||||
rawRgbaBytes,
|
||||
alignmentResult.affineMatrix,
|
||||
alignedImagesFloat32List,
|
||||
@@ -368,14 +382,11 @@ Future<(Float32List, List<AlignmentResult>, List<bool>, List<double>, Size)>
|
||||
RGB _readPixelColor(
|
||||
int x,
|
||||
int y,
|
||||
Image image,
|
||||
Dimensions image,
|
||||
Uint8List rgbaBytes,
|
||||
) {
|
||||
if (y < 0 || y >= image.height || x < 0 || x >= image.width) {
|
||||
if (y < -maxKernelRadius ||
|
||||
y >= image.height + maxKernelRadius ||
|
||||
x < -maxKernelRadius ||
|
||||
x >= image.width + maxKernelRadius) {
|
||||
if (y < -2 || y >= image.height + 2 || x < -2 || x >= image.width + 2) {
|
||||
_logger.severe(
|
||||
'`readPixelColor`: Invalid pixel coordinates, out of bounds. x: $x, y: $y',
|
||||
);
|
||||
@@ -393,29 +404,6 @@ RGB _readPixelColor(
|
||||
);
|
||||
}
|
||||
|
||||
RGB _getPixelBlurred(
|
||||
int x,
|
||||
int y,
|
||||
Image image,
|
||||
Uint8List rgbaBytes,
|
||||
) {
|
||||
double r = 0, g = 0, b = 0;
|
||||
for (int ky = 0; ky < gaussianKernelSize; ky++) {
|
||||
for (int kx = 0; kx < gaussianKernelSize; kx++) {
|
||||
final int px = (x - gaussianKernelRadius + kx);
|
||||
final int py = (y - gaussianKernelRadius + ky);
|
||||
|
||||
final RGB pixelRgbTuple = _readPixelColor(px, py, image, rgbaBytes);
|
||||
final double weight = gaussianKernel[ky][kx];
|
||||
|
||||
r += pixelRgbTuple.$1 * weight;
|
||||
g += pixelRgbTuple.$2 * weight;
|
||||
b += pixelRgbTuple.$3 * weight;
|
||||
}
|
||||
}
|
||||
return (r.round(), g.round(), b.round());
|
||||
}
|
||||
|
||||
List<List<int>> _createGrayscaleIntMatrixFromNormalized2List(
|
||||
Float32List imageList,
|
||||
int startIndex, {
|
||||
@@ -473,7 +461,7 @@ Future<Image> _cropImage(
|
||||
}
|
||||
|
||||
void _warpAffineFloat32List(
|
||||
Image inputImage,
|
||||
Dimensions inputImageDimensions,
|
||||
Uint8List rawRgbaBytes,
|
||||
List<List<double>> affineMatrix,
|
||||
Float32List outputList,
|
||||
@@ -528,8 +516,12 @@ void _warpAffineFloat32List(
|
||||
final num xOrigin = (xTrans - b00) * a00Prime + (yTrans - b10) * a01Prime;
|
||||
final num yOrigin = (xTrans - b00) * a10Prime + (yTrans - b10) * a11Prime;
|
||||
|
||||
final RGB pixel =
|
||||
_getPixelBicubic(xOrigin, yOrigin, inputImage, rawRgbaBytes);
|
||||
final RGB pixel = _getPixelBicubic(
|
||||
xOrigin,
|
||||
yOrigin,
|
||||
inputImageDimensions,
|
||||
rawRgbaBytes,
|
||||
);
|
||||
|
||||
// Set the new pixel
|
||||
outputList[startIndex + 3 * (yTrans * width + xTrans)] =
|
||||
@@ -584,56 +576,14 @@ Future<List<Uint8List>> compressFaceThumbnails(Map args) async {
|
||||
}
|
||||
}
|
||||
|
||||
RGB _getPixelBilinear(
|
||||
RGB _getPixelBicubic(
|
||||
num fx,
|
||||
num fy,
|
||||
Image image,
|
||||
Uint8List rawRgbaBytes, {
|
||||
bool antiAlias = false,
|
||||
}) {
|
||||
// Clamp to image boundaries
|
||||
fx = fx.clamp(0, image.width - 1);
|
||||
fy = fy.clamp(0, image.height - 1);
|
||||
|
||||
// Get the surrounding coordinates and their weights
|
||||
final int x0 = fx.floor();
|
||||
final int x1 = fx.ceil();
|
||||
final int y0 = fy.floor();
|
||||
final int y1 = fy.ceil();
|
||||
final dx = fx - x0;
|
||||
final dy = fy - y0;
|
||||
final dx1 = 1.0 - dx;
|
||||
final dy1 = 1.0 - dy;
|
||||
|
||||
// Get the original pixels (with gaussian blur if antialias)
|
||||
final RGB Function(int, int, Image, Uint8List) readPixel =
|
||||
antiAlias ? _getPixelBlurred : _readPixelColor;
|
||||
final RGB pixel1 = readPixel(x0, y0, image, rawRgbaBytes);
|
||||
final RGB pixel2 = readPixel(x1, y0, image, rawRgbaBytes);
|
||||
final RGB pixel3 = readPixel(x0, y1, image, rawRgbaBytes);
|
||||
final RGB pixel4 = readPixel(x1, y1, image, rawRgbaBytes);
|
||||
|
||||
int bilinear(
|
||||
num val1,
|
||||
num val2,
|
||||
num val3,
|
||||
num val4,
|
||||
) =>
|
||||
(val1 * dx1 * dy1 + val2 * dx * dy1 + val3 * dx1 * dy + val4 * dx * dy)
|
||||
.round();
|
||||
|
||||
// Calculate the weighted sum of pixels
|
||||
final int r = bilinear(pixel1.$1, pixel2.$1, pixel3.$1, pixel4.$1);
|
||||
final int g = bilinear(pixel1.$2, pixel2.$2, pixel3.$2, pixel4.$2);
|
||||
final int b = bilinear(pixel1.$3, pixel2.$3, pixel3.$3, pixel4.$3);
|
||||
|
||||
return (r, g, b);
|
||||
}
|
||||
|
||||
/// Get the pixel value using Bicubic Interpolation. Code taken mainly from https://github.com/brendan-duncan/image/blob/6e407612752ffdb90b28cd5863c7f65856349348/lib/src/image/image.dart#L697
|
||||
RGB _getPixelBicubic(num fx, num fy, Image image, Uint8List rawRgbaBytes) {
|
||||
fx = fx.clamp(0, image.width - 1);
|
||||
fy = fy.clamp(0, image.height - 1);
|
||||
Dimensions imageDimensions,
|
||||
Uint8List rawRgbaBytes,
|
||||
) {
|
||||
fx = fx.clamp(0, imageDimensions.width - 1);
|
||||
fy = fy.clamp(0, imageDimensions.height - 1);
|
||||
|
||||
final x = fx.toInt() - (fx >= 0.0 ? 0 : 1);
|
||||
final px = x - 1;
|
||||
@@ -652,62 +602,69 @@ RGB _getPixelBicubic(num fx, num fy, Image image, Uint8List rawRgbaBytes) {
|
||||
dx * dx * (2 * ipp - 5 * icp + 4 * inp - iap) +
|
||||
dx * dx * dx * (-ipp + 3 * icp - 3 * inp + iap));
|
||||
|
||||
final icc = _readPixelColor(x, y, image, rawRgbaBytes);
|
||||
final icc = _readPixelColor(x, y, imageDimensions, rawRgbaBytes);
|
||||
|
||||
final ipp =
|
||||
px < 0 || py < 0 ? icc : _readPixelColor(px, py, image, rawRgbaBytes);
|
||||
final icp = px < 0 ? icc : _readPixelColor(x, py, image, rawRgbaBytes);
|
||||
final inp = py < 0 || nx >= image.width
|
||||
final ipp = px < 0 || py < 0
|
||||
? icc
|
||||
: _readPixelColor(nx, py, image, rawRgbaBytes);
|
||||
final iap = ax >= image.width || py < 0
|
||||
: _readPixelColor(px, py, imageDimensions, rawRgbaBytes);
|
||||
final icp =
|
||||
px < 0 ? icc : _readPixelColor(x, py, imageDimensions, rawRgbaBytes);
|
||||
final inp = py < 0 || nx >= imageDimensions.width
|
||||
? icc
|
||||
: _readPixelColor(ax, py, image, rawRgbaBytes);
|
||||
: _readPixelColor(nx, py, imageDimensions, rawRgbaBytes);
|
||||
final iap = ax >= imageDimensions.width || py < 0
|
||||
? icc
|
||||
: _readPixelColor(ax, py, imageDimensions, rawRgbaBytes);
|
||||
|
||||
final ip0 = cubic(dx, ipp.$1, icp.$1, inp.$1, iap.$1);
|
||||
final ip1 = cubic(dx, ipp.$2, icp.$2, inp.$2, iap.$2);
|
||||
final ip2 = cubic(dx, ipp.$3, icp.$3, inp.$3, iap.$3);
|
||||
// final ip3 = cubic(dx, ipp.a, icp.a, inp.a, iap.a);
|
||||
|
||||
final ipc = px < 0 ? icc : _readPixelColor(px, y, image, rawRgbaBytes);
|
||||
final inc =
|
||||
nx >= image.width ? icc : _readPixelColor(nx, y, image, rawRgbaBytes);
|
||||
final iac =
|
||||
ax >= image.width ? icc : _readPixelColor(ax, y, image, rawRgbaBytes);
|
||||
final ipc =
|
||||
px < 0 ? icc : _readPixelColor(px, y, imageDimensions, rawRgbaBytes);
|
||||
final inc = nx >= imageDimensions.width
|
||||
? icc
|
||||
: _readPixelColor(nx, y, imageDimensions, rawRgbaBytes);
|
||||
final iac = ax >= imageDimensions.width
|
||||
? icc
|
||||
: _readPixelColor(ax, y, imageDimensions, rawRgbaBytes);
|
||||
|
||||
final ic0 = cubic(dx, ipc.$1, icc.$1, inc.$1, iac.$1);
|
||||
final ic1 = cubic(dx, ipc.$2, icc.$2, inc.$2, iac.$2);
|
||||
final ic2 = cubic(dx, ipc.$3, icc.$3, inc.$3, iac.$3);
|
||||
// final ic3 = cubic(dx, ipc.a, icc.a, inc.a, iac.a);
|
||||
|
||||
final ipn = px < 0 || ny >= image.height
|
||||
final ipn = px < 0 || ny >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(px, ny, image, rawRgbaBytes);
|
||||
final icn =
|
||||
ny >= image.height ? icc : _readPixelColor(x, ny, image, rawRgbaBytes);
|
||||
final inn = nx >= image.width || ny >= image.height
|
||||
: _readPixelColor(px, ny, imageDimensions, rawRgbaBytes);
|
||||
final icn = ny >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(nx, ny, image, rawRgbaBytes);
|
||||
final ian = ax >= image.width || ny >= image.height
|
||||
: _readPixelColor(x, ny, imageDimensions, rawRgbaBytes);
|
||||
final inn = nx >= imageDimensions.width || ny >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(ax, ny, image, rawRgbaBytes);
|
||||
: _readPixelColor(nx, ny, imageDimensions, rawRgbaBytes);
|
||||
final ian = ax >= imageDimensions.width || ny >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(ax, ny, imageDimensions, rawRgbaBytes);
|
||||
|
||||
final in0 = cubic(dx, ipn.$1, icn.$1, inn.$1, ian.$1);
|
||||
final in1 = cubic(dx, ipn.$2, icn.$2, inn.$2, ian.$2);
|
||||
final in2 = cubic(dx, ipn.$3, icn.$3, inn.$3, ian.$3);
|
||||
// final in3 = cubic(dx, ipn.a, icn.a, inn.a, ian.a);
|
||||
|
||||
final ipa = px < 0 || ay >= image.height
|
||||
final ipa = px < 0 || ay >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(px, ay, image, rawRgbaBytes);
|
||||
final ica =
|
||||
ay >= image.height ? icc : _readPixelColor(x, ay, image, rawRgbaBytes);
|
||||
final ina = nx >= image.width || ay >= image.height
|
||||
: _readPixelColor(px, ay, imageDimensions, rawRgbaBytes);
|
||||
final ica = ay >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(nx, ay, image, rawRgbaBytes);
|
||||
final iaa = ax >= image.width || ay >= image.height
|
||||
: _readPixelColor(x, ay, imageDimensions, rawRgbaBytes);
|
||||
final ina = nx >= imageDimensions.width || ay >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(ax, ay, image, rawRgbaBytes);
|
||||
: _readPixelColor(nx, ay, imageDimensions, rawRgbaBytes);
|
||||
final iaa = ax >= imageDimensions.width || ay >= imageDimensions.height
|
||||
? icc
|
||||
: _readPixelColor(ax, ay, imageDimensions, rawRgbaBytes);
|
||||
|
||||
final ia0 = cubic(dx, ipa.$1, ica.$1, ina.$1, iaa.$1);
|
||||
final ia1 = cubic(dx, ipa.$2, ica.$2, ina.$2, iaa.$2);
|
||||
@@ -721,30 +678,3 @@ RGB _getPixelBicubic(num fx, num fy, Image image, Uint8List rawRgbaBytes) {
|
||||
|
||||
return (c0, c1, c2); // (red, green, blue)
|
||||
}
|
||||
|
||||
List<List<double>> create2DGaussianKernel(int size, double sigma) {
|
||||
final List<List<double>> kernel =
|
||||
List.generate(size, (_) => List<double>.filled(size, 0));
|
||||
double sum = 0.0;
|
||||
final int center = size ~/ 2;
|
||||
|
||||
for (int y = 0; y < size; y++) {
|
||||
for (int x = 0; x < size; x++) {
|
||||
final int dx = x - center;
|
||||
final int dy = y - center;
|
||||
final double g = (1 / (2 * pi * sigma * sigma)) *
|
||||
exp(-(dx * dx + dy * dy) / (2 * sigma * sigma));
|
||||
kernel[y][x] = g;
|
||||
sum += g;
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize the kernel
|
||||
for (int y = 0; y < size; y++) {
|
||||
for (int x = 0; x < size; x++) {
|
||||
kernel[y][x] /= sum;
|
||||
}
|
||||
}
|
||||
|
||||
return kernel;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@ import "package:flutter/services.dart";
|
||||
import "package:logging/logging.dart";
|
||||
import "package:photos/core/error-reporting/isolate_logging.dart";
|
||||
import "package:photos/models/base/id.dart";
|
||||
import "package:photos/services/isolate_functions.dart";
|
||||
import "package:photos/utils/isolate/isolate_operations.dart";
|
||||
import "package:synchronized/synchronized.dart";
|
||||
|
||||
@pragma('vm:entry-point')
|
||||
abstract class SuperIsolate {
|
||||
Logger get logger;
|
||||
|
||||
@@ -80,6 +81,8 @@ abstract class SuperIsolate {
|
||||
if (rootToken != null) {
|
||||
BackgroundIsolateBinaryMessenger.ensureInitialized(rootToken);
|
||||
}
|
||||
final logger = Logger('SuperIsolate');
|
||||
logger.info('IsolateMain started');
|
||||
|
||||
receivePort.listen((message) async {
|
||||
final taskID = message[0] as String;
|
||||
@@ -87,6 +90,7 @@ abstract class SuperIsolate {
|
||||
final function = IsolateOperation.values[functionIndex];
|
||||
final args = message[2] as Map<String, dynamic>;
|
||||
final sendPort = message[3] as SendPort;
|
||||
logger.info("Starting isolate operation $function in isolate");
|
||||
|
||||
late final Object data;
|
||||
try {
|
||||
@@ -1,5 +1,6 @@
|
||||
import "dart:io" show File, Platform;
|
||||
import "dart:math" as math show sqrt, min, max;
|
||||
import "dart:typed_data" show Uint8List;
|
||||
|
||||
import "package:flutter/services.dart" show PlatformException;
|
||||
import "package:logging/logging.dart";
|
||||
@@ -22,6 +23,8 @@ import "package:photos/services/machine_learning/ml_result.dart";
|
||||
import "package:photos/services/machine_learning/semantic_search/semantic_search_service.dart";
|
||||
import "package:photos/services/search_service.dart";
|
||||
import "package:photos/services/sync/local_sync_service.dart";
|
||||
import "package:photos/src/rust/api/image_processing.dart";
|
||||
import "package:photos/src/rust/custom/init_frb.dart";
|
||||
import "package:photos/utils/file_util.dart";
|
||||
import "package:photos/utils/image_ml_util.dart";
|
||||
import "package:photos/utils/network_util.dart";
|
||||
@@ -410,27 +413,60 @@ Future<MLResult> analyzeImageStatic(Map args) async {
|
||||
_logger.info(
|
||||
"Start analyzeImageStatic for fileID $enteFileID (runFaces: $runFaces, runClip: $runClip)",
|
||||
);
|
||||
await initFrb();
|
||||
final startTime = DateTime.now();
|
||||
|
||||
// Decode the image once to use for both face detection and alignment
|
||||
final decodedImage =
|
||||
await decodeImageFromPath(imagePath, includeRgbaBytes: true);
|
||||
final image = decodedImage.image;
|
||||
final rawRgbaBytes = decodedImage.rawRgbaBytes!;
|
||||
final bool decodeInRust = canRustDecodeImage(imagePath);
|
||||
late (
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt,
|
||||
Uint8List,
|
||||
BigInt,
|
||||
BigInt
|
||||
) rustResults;
|
||||
if (decodeInRust) {
|
||||
rustResults = await processImageMlFromPath(imagePath: imagePath);
|
||||
} else {
|
||||
final (image, decodedRgbaBytes) = await decodeImageFromPath(imagePath);
|
||||
rustResults = await processImageMlFromData(
|
||||
rgbaData: decodedRgbaBytes,
|
||||
width: image.width,
|
||||
height: image.height,
|
||||
);
|
||||
}
|
||||
final (
|
||||
rawRgbaBytes,
|
||||
imageHeight,
|
||||
imageWidth,
|
||||
faceBytes,
|
||||
faceHeight,
|
||||
faceWidth,
|
||||
clipBytes,
|
||||
clipHeight,
|
||||
clipWidth
|
||||
) = rustResults;
|
||||
final decodedImageSize =
|
||||
Dimensions(height: image.height, width: image.width);
|
||||
final result = MLResult.fromEnteFileID(enteFileID);
|
||||
result.decodedImageSize = decodedImageSize;
|
||||
Dimensions(height: imageHeight.toInt(), width: imageWidth.toInt());
|
||||
final decodeTime = DateTime.now();
|
||||
final decodeMs = decodeTime.difference(startTime).inMilliseconds;
|
||||
|
||||
final result = MLResult.fromEnteFileID(enteFileID);
|
||||
result.decodedImageSize = decodedImageSize;
|
||||
String faceMsString = "", clipMsString = "";
|
||||
final pipelines = await Future.wait([
|
||||
runFaces
|
||||
? FaceRecognitionService.runFacesPipeline(
|
||||
enteFileID,
|
||||
image,
|
||||
decodedImageSize,
|
||||
rawRgbaBytes,
|
||||
faceBytes,
|
||||
faceHeight.toInt(),
|
||||
faceWidth.toInt(),
|
||||
faceDetectionAddress,
|
||||
faceEmbeddingAddress,
|
||||
).then((result) {
|
||||
@@ -442,8 +478,9 @@ Future<MLResult> analyzeImageStatic(Map args) async {
|
||||
runClip
|
||||
? SemanticSearchService.runClipImage(
|
||||
enteFileID,
|
||||
image,
|
||||
rawRgbaBytes,
|
||||
clipBytes,
|
||||
clipHeight.toInt(),
|
||||
clipWidth.toInt(),
|
||||
clipImageAddress,
|
||||
).then((result) {
|
||||
clipMsString =
|
||||
|
||||
@@ -58,8 +58,6 @@ class FlagService {
|
||||
|
||||
bool get enableMobMultiPart => flags.enableMobMultiPart || internalUser;
|
||||
|
||||
bool get enableVectorDb => flags.internalUser;
|
||||
|
||||
String get castUrl => flags.castUrl;
|
||||
|
||||
bool hasSyncedAccountFlags() {
|
||||
|
||||
@@ -5,10 +5,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834
|
||||
sha256: "16e298750b6d0af7ce8a3ba7c18c69c3785d11b15ec83f6dcd0ad2a0009b3cab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "72.0.0"
|
||||
version: "76.0.0"
|
||||
_flutterfire_internals:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -21,7 +21,7 @@ packages:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.2"
|
||||
version: "0.3.3"
|
||||
adaptive_theme:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -34,10 +34,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139
|
||||
sha256: "1f14db053a8c23e260789e9b0980fa27f2680dd640932cae5e1137cce0e46e1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.7.0"
|
||||
version: "6.11.0"
|
||||
android_intent_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -130,10 +130,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: async
|
||||
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
|
||||
sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.11.0"
|
||||
version: "2.12.0"
|
||||
battery_info:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -155,10 +155,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
brotli:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -276,10 +276,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
version: "1.4.0"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -309,10 +309,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
version: "1.1.2"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -325,10 +325,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: collection
|
||||
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
version: "1.19.1"
|
||||
computer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -627,10 +627,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
|
||||
sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
version: "1.3.2"
|
||||
fast_base58:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -647,6 +647,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
ffigen:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: ffigen
|
||||
sha256: a0ca4853028c6a9e4d9a0a40bb744fceb898c89d75931d08e87b3987d0087060
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.0"
|
||||
ffmpeg_kit_flutter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -676,10 +684,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
version: "7.0.1"
|
||||
file_saver:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -782,7 +790,7 @@ packages:
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
flutter_driver:
|
||||
dependency: "direct dev"
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
@@ -914,14 +922,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_launcher_icons
|
||||
sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.1"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@@ -1432,18 +1432,18 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
||||
sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.5"
|
||||
version: "10.0.8"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.5"
|
||||
version: "3.0.9"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1552,10 +1552,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: macros
|
||||
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
|
||||
sha256: "1d9e801cd66f7ea3663c45fc708450db1fa57f988142c64289142c9b7ee80656"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2-main.4"
|
||||
version: "0.1.3-main.0"
|
||||
maps_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1568,10 +1568,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.16+1"
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1661,10 +1661,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.15.0"
|
||||
version: "1.16.0"
|
||||
mgrs_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1875,10 +1875,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: path
|
||||
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
version: "1.9.1"
|
||||
path_drawing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -2035,10 +2035,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.5"
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -2064,7 +2064,7 @@ packages:
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
pool:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: pool
|
||||
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||
@@ -2084,10 +2084,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: process
|
||||
sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32"
|
||||
sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.2"
|
||||
version: "5.0.3"
|
||||
proj4dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -2097,7 +2097,7 @@ packages:
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
protobuf:
|
||||
dependency: "direct overridden"
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: protobuf
|
||||
sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
|
||||
@@ -2105,7 +2105,7 @@ packages:
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
provider:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: provider
|
||||
sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c
|
||||
@@ -2129,7 +2129,7 @@ packages:
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
quiver:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: quiver
|
||||
sha256: ea0b925899e64ecdfbf9c7becb60d5b50e706ade44a85b2363be2a22d88117d2
|
||||
@@ -2332,7 +2332,7 @@ packages:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.99"
|
||||
version: "0.0.0"
|
||||
source_gen:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -2369,10 +2369,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
|
||||
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.0"
|
||||
version: "1.10.1"
|
||||
sprintf:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -2457,10 +2457,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.11.1"
|
||||
version: "1.12.1"
|
||||
step_progress_indicator:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -2473,10 +2473,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.4"
|
||||
stream_transform:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -2489,10 +2489,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
version: "1.4.1"
|
||||
styled_text:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -2545,34 +2545,34 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
version: "1.2.2"
|
||||
test:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: test
|
||||
sha256: "7ee44229615f8f642b68120165ae4c2a75fe77ae2065b1e55ae4711f6cf0899e"
|
||||
sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.25.7"
|
||||
version: "1.25.15"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.2"
|
||||
version: "0.7.4"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: "55ea5a652e38a1dfb32943a7973f3681a60f872f8c3a05a14664ad54ef9c6696"
|
||||
sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.4"
|
||||
version: "0.6.8"
|
||||
thermal:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -2836,10 +2836,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
|
||||
sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.5"
|
||||
version: "14.3.1"
|
||||
volume_controller:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -2900,10 +2900,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webdriver
|
||||
sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e"
|
||||
sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
version: "3.0.4"
|
||||
webkit_inspection_protocol:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -3000,6 +3000,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
yaml_edit:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml_edit
|
||||
sha256: fb38626579fb345ad00e674e2af3a5c9b0cc4b9bfb8fd7f7ff322c7c9e62aef5
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
sdks:
|
||||
dart: ">=3.5.0 <4.0.0"
|
||||
dart: ">=3.7.0-0 <4.0.0"
|
||||
flutter: ">=3.24.0"
|
||||
|
||||