Compare commits

..

334 Commits

Author SHA1 Message Date
laurenspriem
7d1845fbd6 Fix deprecated Share usage by using SharePlus.instance.share() 2025-09-02 14:19:19 +05:30
laurenspriem
9abce0883b Merge branch 'main' into faces_growup 2025-09-02 14:05:36 +05:30
Neeraj
0d1f20f9e2 [mob][photos] Clear up flutter analyze (#7035)
## Description

- Replace withOpacity() with withValues(alpha:)
- Replace onPopInvoked with onPopInvokedWithResult
- Update MaterialState references to WidgetState
- Organize imports
- Remove unneeded nullability
- Dangling library docs
- collectionName deprecation warning
- TextInputWidget isPasswordInput deprecation warning
2025-09-02 12:29:26 +05:30
Ashil
c55447a08f [mob][debug] To debug thumbnail not loading (#7036) 2025-09-02 12:28:34 +05:30
Ashil
98d56e8fa4 Merge branch 'main' into thunmbail_issue_debug 2025-09-02 12:26:48 +05:30
ashilkn
f244c94ebf Update internal change log 2025-09-02 12:24:01 +05:30
laurenspriem
88f2b88f4d Remove deprecation warnings 2025-09-02 12:15:07 +05:30
Neeraj
db1fef40db [mob] Update changelog (#7034)
## Description

## Tests
2025-09-02 12:14:36 +05:30
laurenspriem
1fd29cdd13 dangling library doc 2025-09-02 12:02:53 +05:30
laurenspriem
947d294afe non nullable dialog 2025-09-02 12:02:36 +05:30
ashilkn
515715660e Add option to config local thumbnail queue to debug thumbnail not displaying issue + add more logging + show local ID of file on thumbnails (configurable) 2025-09-02 11:53:21 +05:30
laurenspriem
324221171d organize imports 2025-09-02 11:50:01 +05:30
laurenspriem
f5f2ff1b2c Fix Flutter deprecation warnings
- Replace withOpacity() with withValues(alpha:)
- Replace onPopInvoked with onPopInvokedWithResult
- Update MaterialState references to WidgetState

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-02 11:49:12 +05:30
Neeraj Gupta
244d41621c Bump version 2025-09-02 11:41:40 +05:30
Neeraj Gupta
91b6a08a35 Update changelog entries with new features
- Replace old changelog entries with new ones across all supported languages
- Add Similar Images, Manual video stream generation, and Performance Improvements features
- Remove outdated entries for Advanced Image Editor, Smart Albums, Improved Gallery, and Faster Scroll

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-02 11:39:54 +05:30
Neeraj
770a311da5 [auth] Fix manual app lock with macos touch id (#6793)
## Description
This fixes https://github.com/ente-io/ente/issues/3428
This was broken because of
https://github.com/eaceto/flutter_local_authentication/issues/8
I've also added that if the app is locked manually, the macOS Touch ID
API won't be called until the user either presses the unlock button
again or unfocuses the app and then focuses back on it. This behavior
also applies when the app window is closed and then reopened.
2025-09-02 10:46:56 +05:30
Neeraj
db76dee639 fix: only show when video streaming is enabled (#7031)
## Description

## Tests
2025-09-02 10:45:37 +05:30
laurenspriem
f54e08bd62 Fix unused import in face_timeline.dart 2025-09-02 10:06:46 +05:30
Manav Rathi
20ce760e85 feat(rust): Initialize Rust CLI foundation (#6915)
## Summary
Rust CLI achieves feature parity with Go CLI for photos app core
functionality

## Changes
- Export, sync, and incremental updates working
- Hash-based deduplication and live photo support
- Public magic metadata for renamed files
- Progress indicators for downloads

## Remaining
- Export filters (album, date range)
- Resume interrupted downloads
- Shared/hidden album support
2025-09-02 09:57:56 +05:30
laurenspriem
aee62b6e64 Add UI components and integrate Faces Through Time with PeoplePage 2025-09-02 09:56:21 +05:30
laurenspriem
cabb770958 Implement core data models and service for Faces Through Time feature 2025-09-02 09:41:17 +05:30
Prateek Sunal
df1bfbe839 fix: initialize compute controllers async with values 2025-09-02 02:51:09 +05:30
Prateek Sunal
27d72eb821 fix: make continuation and releasing compute better 2025-09-02 02:44:11 +05:30
Prateek Sunal
98786c5824 fix: move logs at better place 2025-09-02 01:04:24 +05:30
Prateek Sunal
d38a09c3f0 perf: optimize video stream processing state management
- Move isCurrentlyProcessing to widget state for better performance
- Only call setState when processing state actually changes
- Add comprehensive processing status handling (retry, compressing, uploading)
- Remove redundant service calls from build method
- Clean up unnecessary early returns and duplicate logic

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-01 18:50:00 +00:00
Aman Raj Singh Mourya
91785d8c90 Add Parallels custom icon (#7026)
## Description
This PR adds a custom icon for Parallels.

- Added `parallels.svg` under
`mobile/apps/auth/assets/custom-icons/icons/`
- Updated `custom-icons.json` with:
  - title: "Parallels"
  - slug: "parallels"
  - hex: #E61E25
  - altNames: ["Parallels Desktop", "Parallels VM"]

The icon is optimized (well under 50KB) and uses the official Parallels
red (#E61E25).
2025-09-02 00:19:46 +05:30
Prateek Sunal
b1f28e3f2e chore: update locks 2025-09-01 23:35:43 +05:30
Prateek Sunal
c155bdd058 chore: lint fixes 2025-09-01 23:35:30 +05:30
Prateek Sunal
a859f28e2c fix: show queueed or creatingStream based on context 2025-09-01 23:35:25 +05:30
Prateek Sunal
8d75528aa5 fix: introduce in queue and creating stream two types of statuses 2025-09-01 23:35:08 +05:30
Prateek Sunal
7f43c11985 fix: only show when video streaming is enabled 2025-09-01 21:22:17 +05:30
Manav Rathi
aadda7e3f6 feat(export): Add file deletion and rename detection to match Go CLI
Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-01 18:01:01 +05:30
laurenspriem
943c6ab585 Add Faces Through Time feature design and implementation docs 2025-09-01 17:34:22 +05:30
Ashil
210c18d244 Update internal changes (#7030) 2025-09-01 17:32:12 +05:30
Ashil
6636849838 update internal changes 2025-09-01 17:26:20 +05:30
Neeraj
5500315351 [mob][photos] fix unsupported locales in language selector (#7029) 2025-09-01 17:24:32 +05:30
Prateek Sunal
562292e642 fix: remove unsupported languages from language picker
Remove languages from _getLanguageName that don't have >90% translation
coverage and aren't in appSupportedLocales (Finnish, Korean, Arabic).
Also improve Chinese locale display.

- Removed fi, ko, ar cases that don't meet translation threshold
- Fixed Chinese locale handling to properly show "中文 (简体)" for zh_CN
- Ensures only properly translated languages appear in the picker

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-01 17:13:58 +05:30
Prateek Sunal
4aa80edbcf fix: resolve unsupported locales appearing in language selector
Replace AppLocalizations.supportedLocales with a curated list of properly
translated locales in the Photos app. This fixes the issue where unsupported
language codes (Bg, Be, Ca, Cs, etc.) were appearing in the language selector
without proper language name formatting.

- Add custom appSupportedLocales list with only >90% translated languages
- Update all references throughout Photos app to use the custom locale list
- Ensures only properly supported languages appear in the language picker

Co-authored-by: Claude <noreply@anthropic.com>
2025-09-01 17:02:15 +05:30
Neeraj
9524a639cd [server] Fix collection link for locker (#6961)
## Description

## Tests
2025-09-01 16:28:19 +05:30
Neeraj
b8eb793c16 [mobile/photos] New translations (#7022)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-app)
2025-09-01 16:26:51 +05:30
Ashil
4b514f1e1a [mob][photos] Revert "Revert diskLoadDeferDuration to 500ms" (#7028)
This reverts commit a295f223b6.
2025-09-01 16:25:59 +05:30
eYdr1en
bee2bb9621 remove unusuded variable 2025-09-01 12:45:27 +02:00
ashilkn
772121c22e Revert "Revert diskLoadDeferDuration to 500ms"
This reverts commit a295f223b6.
2025-09-01 16:07:40 +05:30
eYdr1en
3c49ca0f6e Merge branch 'main' into touch-id 2025-09-01 12:35:27 +02:00
Onurcan
f2e51893ad Update Parallels custom-icons.json 2025-09-01 11:59:32 +03:00
Onurcan
c08b78c775 dd Parallels custom icon 2025-09-01 11:58:26 +03:00
Manav Rathi
233f03355f Fix security issues and match Go CLI error handling
Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-01 13:16:03 +05:30
Laurens Priem
73ab50f113 [mob][photos] Run vectorDB migration is memory safe way (#7024)
## Description

- Add ability to block computeController temporarily
- Block computeController when vectorDB migration is running
2025-09-01 11:44:39 +05:30
laurenspriem
4a2346fe93 Block compute when vectorDB migration is happening 2025-09-01 11:11:10 +05:30
laurenspriem
68b5cce158 Add option to block compute tasks (ml, streaming) 2025-09-01 11:09:51 +05:30
laurenspriem
e907a9e8cb comment 2025-09-01 10:59:02 +05:30
Manav Rathi
92a40afca2 [web] New translations (#7021)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-web)
2025-09-01 10:22:50 +05:30
Aman Raj Singh Mourya
0c2b38c059 Fixing dev build on macos (#7012)
This should fix this error
https://github.com/ente-io/ente/pull/6768#discussion_r2310164866
2025-09-01 09:26:51 +05:30
Crowdin Bot
19650bcd57 New Crowdin translations by GitHub Action 2025-09-01 01:05:33 +00:00
Crowdin Bot
2b9ca073ce New Crowdin translations by GitHub Action 2025-09-01 00:45:39 +00:00
Manav Rathi
2257087bb2 Fix file rename handling to match Go CLI behavior
- Add rename detection by tracking files via ID in metadata
- Remove old files (including live photo MOV components) when renamed
- Copy live photo MOV components during hash deduplication
- Preserve file deduplication optimization while handling renames correctly

This ensures that when a file is renamed in Ente, the old file is removed
and replaced with the renamed version, matching the Go CLI's behavior.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 21:56:14 +05:30
Manav Rathi
2a5bce2ae4 Fix live photo export to preserve original file extensions
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 10:36:25 +05:30
Manav Rathi
1e0a6eb1ea Add persistent storage for public magic metadata
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 09:28:00 +05:30
Manav Rathi
187a729013 Update CLAUDE.md documentation to reflect current codebase
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 09:10:50 +05:30
Manav Rathi
c98f4dfffd fix(rust): Match Go CLI email filtering behavior
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 07:39:59 +05:30
Manav Rathi
4140a0f6fe feat(rust): Add shared album decryption support
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-31 06:54:33 +05:30
Aman Raj Singh Mourya
cf4b87dad9 [locker] Refactor theme handling in Locker to fix DynamicFAB style (#7016)
## Description
DynamicFAB theme was not getting applied from the commons package. This
PR fix that issue.

## Tests

**Before**

<img width="300" height="750" alt="Simulator Screenshot - iPhone 16 Plus
- 2025-08-30 at 15 53 26"
src="https://github.com/user-attachments/assets/17dfc778-b652-4e10-ad8f-3c8aed2656f6"
/>


**After**

<img width="300" height="750" alt="Simulator Screenshot - iPhone 16 Plus
- 2025-08-30 at 15 52 58"
src="https://github.com/user-attachments/assets/9e0c2feb-8204-4875-9bad-f9d4eaab8f36"
/>
2025-08-30 16:01:01 +05:30
AmanRajSinghMourya
3fd0db6a90 Refactor theme handling in locker to fix DynamicFAB 2025-08-30 15:52:34 +05:30
Manav Rathi
ac68b99ecf Fix shared collection deserialization
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-30 10:53:44 +05:30
Manav Rathi
82e1a0e358 Fix hidden album filtering to match Go CLI
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-30 10:37:45 +05:30
Laurens Priem
ce1701d211 [mob][photos] Similar small fixes (#7008)
## Description

Small design changes and fixes.

## Tests

Tested in debug mode on my pixel phone.
2025-08-30 07:29:27 +05:30
Manav Rathi
034e789242 fix(rust): Validate account exists before update/delete
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 22:02:35 +05:30
Manav Rathi
ccfec4071f fix(rust): Match Go CLI JSON field naming for ID fields
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 21:51:34 +05:30
Manav Rathi
c4830732fd fix(rust): Format timestamps as ISO 8601 in metadata JSON
Changed metadata export to match Go CLI's timestamp format.
Timestamps now serialize as ISO 8601 strings with timezone offset
(e.g., "2025-07-23T19:48:06.098+05:30") instead of Unix microseconds.

Also fixed clippy warnings to ensure CI compliance.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 21:41:33 +05:30
Manav Rathi
72dc56e41f fix(rust): Correct album-based export to match Go CLI
Fixed export to properly organize files into album folders by:
- Fetching files from all collections using /collections/v2/diff endpoint
- Decrypting encrypted collection names to get actual album names
- Using decrypted album names for folder organization

Files now export to proper album folders instead of all going to
"Uncategorized". Tested and verified with local data.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 21:20:30 +05:30
Laurens Priem
aaed336991 [infra] Release action changes (#7010)
## Description

- Disk space cleanup
- Generate rust bindings
2025-08-29 20:05:31 +05:30
eYdr1en
0b85dfe7e4 fixing dev build on macos 2025-08-29 15:53:56 +02:00
Prateek Sunal
68422b172f [mob][photos] fix manual video streaming when ML is enabled (#7009) 2025-08-29 18:53:53 +05:30
laurenspriem
db99dae3e1 log line 2025-08-29 18:48:00 +05:30
laurenspriem
3717a156d3 Logging unexpected embeddings 2025-08-29 18:47:01 +05:30
Prateek Sunal
ca9930e01b style: fix import directive ordering in thumbnail_widget 2025-08-29 12:50:13 +00:00
laurenspriem
eb23a4e770 rust bindings 2025-08-29 18:19:50 +05:30
laurenspriem
e03303e5b3 release workflow disk cleanup 2025-08-29 18:18:45 +05:30
laurenspriem
2ad27f1c6e Clear similar images json cache 2025-08-29 18:03:27 +05:30
Prateek Sunal
202e6a9f7c fix: trigger processing for already-queued manual stream files
When users click "Create Stream" on files already in queue from
previous sessions, ensure processing actually starts even if the
file was previously stalled due to ML blocking.

Add forceProcess parameter to queueFiles() to bypass the existing
queue check and trigger processing of stalled manual queue items.
2025-08-29 12:28:48 +00:00
laurenspriem
ceaedad327 debug option to delete vectorDB index 2025-08-29 17:57:21 +05:30
Prateek Sunal
fd963a1c8e fix: allow manual video stream creation when ML is waiting
When ML is enabled but not running, the compute controller blocks
all stream requests due to _waitingToRunML flag. This prevents
users from manually creating video streams even though ML isn't
actively using resources.

Add bypassMLWaiting parameter to allow manual stream creation
to proceed regardless of ML waiting state, improving UX.
2025-08-29 12:22:08 +00:00
laurenspriem
b40b5bb1ae delete progress 2025-08-29 17:07:44 +05:30
laurenspriem
91827626b2 dot dot dot 2025-08-29 17:04:33 +05:30
laurenspriem
42318335ae Fix issue with deleting favorites 2025-08-29 16:16:51 +05:30
laurenspriem
858db62385 Left align large files 2025-08-29 16:09:10 +05:30
laurenspriem
46e36612d3 Scroll to top after delete 2025-08-29 15:49:09 +05:30
laurenspriem
62cf236e3b Cycle through loading screen texts 2025-08-29 15:27:11 +05:30
Ashil
c2b1ab86f2 [mob][photos] Fix incorrect file deletion from db when widget unmounts during thumbnail loading (#7007)
## Description

Previously, when _loadWithRetry returned null due to widget unmounting,
the code incorrectly assumed the local file was deleted and would remove
database reference of the file and which would trigger re-upload of the
file.
2025-08-29 15:17:55 +05:30
laurenspriem
43adf42281 Don't auto select favorites for deletions 2025-08-29 15:00:42 +05:30
laurenspriem
1e2a65281c Fix delete button bug 2025-08-29 14:32:12 +05:30
ashilkn
70eb68b13c Fix incorrect file deletion when widget unmounts during thumbnail loading
Previously, when _loadWithRetry returned null due to widget unmounting,
the code incorrectly assumed the local file was deleted and would remove
database references or delete the file. This could lead to data loss.

Changes:
- Add new WidgetUnmountedException to centralized exceptions.dart for reuse
- Throw WidgetUnmountedException instead of returning null when widget unmounts
- Handle WidgetUnmountedException separately in error handler with appropriate logging
- Still set _errorLoadingLocalThumbnail flag to prevent retry attempts

Using Exception instead of Error follows Dart conventions:
- Exceptions are for recoverable runtime conditions (like widget unmounting)
- Errors are for programming mistakes that shouldn't be caught

This ensures that widget unmounting is properly distinguished from actual
file access failures.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 14:17:49 +05:30
Ashil
fa86b19307 [mob][photos] Update internal change log (#7006) 2025-08-29 14:10:19 +05:30
Ashil
e632dc7771 Merge branch 'main' into update_int_change_log 2025-08-29 14:08:58 +05:30
ashilkn
7fa9adb636 update internal change log 2025-08-29 14:08:00 +05:30
Ashil
83f885f158 [mob][photos] Revert diskLoadDeferDuration to 500ms (#7005)
## Description
2025-08-29 13:58:58 +05:30
ashilkn
a295f223b6 Revert diskLoadDeferDuration to 500ms
Reverts the change from commit 1f1cad181f
which reduced galleryThumbnailDiskLoadDeferDuration from 500ms to 80ms.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 12:57:42 +05:30
Laurens Priem
cc64ef8035 [infra] Create more space for internal release action (#7004)
## Description

- Removed redundant SDKs
- Removed redundant rust install (already pre-installed)
- Delete old other action
2025-08-29 12:41:28 +05:30
Ashil
69dd7b6233 [mob][photos] Spacing (#7002)
## Description

Add spacing in similar images page.
2025-08-29 12:41:11 +05:30
Ashil
bcc9f1be73 [mob][photos] Revert cache extent changes (#7000)
## Description

The reverted changes were intended to solve the issue #6957 fixed. So
these changes are no longer needed and there are doubts if they are
causing regressions related to thumbnail loading.

## Tests
2025-08-29 12:26:29 +05:30
Ashil
296b2a2a6c Merge branch 'main' into revert-cache-extent-changes 2025-08-29 12:24:03 +05:30
laurenspriem
6b48c9bc34 Remove ineffective cleanup steps 2025-08-29 12:23:47 +05:30
ashilkn
6a951bcc72 Update internal change log 2025-08-29 12:23:28 +05:30
laurenspriem
38914981a1 Fix disk space calculation in cleanup step 2025-08-29 11:52:36 +05:30
laurenspriem
66f4d5b1a6 Add disk cleanup step to free space in GitHub Actions
Removes unused pre-installed software to free ~30-45GB:
- .NET SDK (~20-25GB)
- Haskell compiler (~5-8GB)
- Boost libraries (~1-2GB)
- Cached tool versions (~5-10GB)

Includes timing and space metrics for each removal

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-29 11:43:07 +05:30
laurenspriem
9ee3781320 spacing 2025-08-29 10:30:05 +05:30
laurenspriem
907d1d2bb8 delete rust install (already on runner env by default) 2025-08-29 10:27:46 +05:30
laurenspriem
8218283463 delete old action 2025-08-29 10:26:32 +05:30
Laurens Priem
bd43385949 [mob][photos] Similar index clear (#6997)
## Description

- Clear vectorDB index on logout
- Revert to using `view` on index
- Use `.usearch` for index file
- Minor design changes

## Tests

Tested in debug mode on my pixel phone.
2025-08-28 18:57:28 +05:30
Laurens Priem
2e6a9acaf9 Merge branch 'main' into similar_index_clear 2025-08-28 18:57:11 +05:30
Ashil
a02dcace7d [mob][photos] New Ducky launcher icons (#6999) 2025-08-28 18:56:34 +05:30
Laurens Priem
cf4285de6d Merge branch 'main' into similar_index_clear 2025-08-28 18:50:12 +05:30
laurenspriem
f831491e4a log changes 2025-08-28 18:30:29 +05:30
laurenspriem
af154d82de ducky analyzing riv animation 2025-08-28 18:17:11 +05:30
Prateek Sunal
ff2f75ea74 [mob][photos] bypass size/duration limits for manual video stream requests (#6998)
## Summary
- Modified `_checkFileForPreviewCreation` method to accept `isManual`
parameter
- Bypass 500MB file size and 60 second duration limits when user
manually triggers video stream processing
- Maintains size/duration restrictions for automatic streaming to
preserve device performance

## Test plan
- [x] Manual Create/Recreate Stream button bypasses 500MB and 60 second
limits
- [x] Automatic streaming still respects size and duration restrictions
- [x] Files larger than 500MB or longer than 60 seconds can be manually
processed
2025-08-28 17:51:26 +05:30
laurenspriem
97e3ef819a Change migration key because index file was changed (internal only) 2025-08-28 17:43:18 +05:30
Prateek Sunal
3685cd2154 fix: don't show create stream if file size is null 2025-08-28 17:34:29 +05:30
laurenspriem
c64fff8ca4 empty tab state ducky 2025-08-28 17:25:54 +05:30
ashilkn
23dc809589 Remove hardcoded cacheExtent to use Flutter's default value 2025-08-28 17:07:46 +05:30
laurenspriem
33d1242c6d strings 2025-08-28 17:03:53 +05:30
laurenspriem
b8ee9fafd1 Better confirmation dialog 2025-08-28 17:03:24 +05:30
ashilkn
f72c9fa068 Revert "Different cache extents for different photoGridSizes"
This reverts commit 769adb75c5.
2025-08-28 16:55:39 +05:30
Ashil
1a7275a101 Merge branch 'main' into ducky_icon 2025-08-28 16:17:58 +05:30
Prateek Sunal
fa7ccbd180 fix: if fileSize is null for manual way then skip 10MB check 2025-08-28 16:16:15 +05:30
ashilkn
79e26d6993 Update internal change log 2025-08-28 16:16:04 +05:30
ashilkn
023135afb5 Remove green background from ducky icon display
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-28 16:07:32 +05:30
Prateek Sunal
04aaa3a5e4 fix: bypass size/duration limits for manual video stream requests
Allow manual stream requests to bypass the 500MB file size and 60-second
duration limits by passing isManual parameter to _checkFileForPreviewCreation.
This ensures users can manually process large files even if they exceed the
automatic streaming limits.
2025-08-28 10:25:45 +00:00
laurenspriem
848857f409 same close related 2025-08-28 15:27:33 +05:30
ashilkn
137033be67 Update Android ducky launcher icon
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-28 15:23:51 +05:30
laurenspriem
04b6f4a765 right threshold 2025-08-28 15:05:58 +05:30
laurenspriem
2645ba0949 Change index file name to use usearch 2025-08-28 13:13:16 +05:30
laurenspriem
5958647fa8 Delete vectorDB index file on logout 2025-08-28 13:11:05 +05:30
laurenspriem
b7b91631f6 revert back to view on index 2025-08-28 13:01:26 +05:30
Prateek Sunal
67d7f586b2 [mob][photos] bypass interaction check for manual stream requests (#6993)
## Summary
- Manual Create/Recreate Stream button presses now bypass user
interaction timer for immediate processing
- Fixed multiple concurrent streaming processes bug in ComputeController
- Fixed video streaming description text display spacing in advanced
settings
- Maintains device health and ML priority checks for all streaming
requests

## Tests
- [x] Manual Create/Recreate Stream button bypasses interaction timer  
- [x] Automatic streaming still respects interaction timer
- [x] Only one streaming process allowed at a time
2025-08-27 21:15:55 +05:30
Prateek Sunal
7c22a8bb25 chore: lint fix 2025-08-27 21:10:18 +05:30
Prateek Sunal
ff3864a09a fix: check only if permission granted before chunking 2025-08-27 21:09:10 +05:30
Prateek Sunal
4484b9e4ad update: add video streaming improvements to change logs
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-27 13:21:59 +00:00
Prateek Sunal
e9554ffbcb fix: prevent multiple concurrent streaming processes
Remove condition allowing additional stream requests when already streaming to ensure only one stream process runs at a time.

Co-authored-by: Claude <noreply@anthropic.com>
2025-08-27 13:15:10 +00:00
Prateek Sunal
ad3901d484 fix: remove conditional clearQueue for manual processing
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-27 13:08:12 +00:00
Prateek Sunal
ecca4c3dc8 feat: bypass interaction check for manual stream requests
Co-authored-by: Claude <noreply@anthropic.com>
2025-08-27 13:00:21 +00:00
Prateek Sunal
d05521f884 [mob][photos] video streaming description spacing and alignment (#6992)
## Summary
- Split videoStreamingDescription into separate line1/line2 localization
keys
- Remove TextAlign.justify from enabled state to fix awkward word
spacing
- Standardize text rendering between enabled and disabled states
- Both states now display description consistently without spacing
issues

## Test plan
- [x] Verify enabled state displays as single line without spacing
issues
- [x] Verify disabled state shows proper line breaks in onboarding
- [x] Confirm localization keys generate correctly
- [x] Run dart format and dart analyze (no issues)

Fixes video streaming settings page text display inconsistencies.
2025-08-27 18:08:28 +05:30
Prateek Sunal
ff37c4bf81 fix: video streaming description spacing and alignment
- Split videoStreamingDescription into separate line1/line2 localization keys
- Remove TextAlign.justify from enabled state to fix awkward word spacing
- Standardize text rendering between enabled and disabled states
- Both states now display description consistently without spacing issues

Co-authored-by: Claude <noreply@anthropic.com>
2025-08-27 12:03:31 +00:00
ashilkn
446df755fa Add ducky icon for iOS
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-27 17:23:31 +05:30
Manav Rathi
0f5e30e96b feat(rust): Add metadata export matching Go CLI format
Export album and file metadata to .meta folders within each album directory.
Enables incremental sync and compatibility with Go CLI exports.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-27 17:22:17 +05:30
Manav Rathi
35ded7bc59 fix(rust): Match Go CLI's album-based export directory structure
Switch from date-based (YYYY/MM-Month) to album-based directory structure
to ensure compatibility with Go CLI. Files now export to AlbumName/ folders
with "Uncategorized" for files without albums.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-27 17:06:40 +05:30
ashilkn
a7805784b7 Add ducky icon for Android
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-27 17:01:09 +05:30
Manav Rathi
8e3f6e56d2 feat(rust): Remove sync command to match Go CLI interface
Align with Go CLI by integrating sync into export workflow.
Update CLAUDE.md to prevent default template usage in commits.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-27 16:51:04 +05:30
Neeraj
84a5ad0b86 [mob][photos] More minor design changes for similar images (#6986)
## Description

- Change tab order
- Make tabs distinct
- Change default ordering to size

## Tests

Tested in debug mode on my pixel phone.
2025-08-27 13:25:40 +05:30
laurenspriem
44ad11343a Better empty state prompt for emtpy tab 2025-08-27 13:17:41 +05:30
laurenspriem
07e50e3cfe Change default sort to size 2025-08-27 13:03:30 +05:30
laurenspriem
df8bbdb788 Make identical and similar distinct 2025-08-27 13:01:56 +05:30
laurenspriem
1ed381fe52 Change order of tabs 2025-08-27 12:52:45 +05:30
ashilkn
55090436ce Add new ducky-icon assets 2025-08-27 11:21:36 +05:30
Manav Rathi
150534aa1a feat(rust): Add deduplication, live photos, and update docs
- Hash-based file deduplication prevents duplicate exports
- Live photo extraction from ZIP archives
- Update conversion status documenting feature completion
- Make commit guidelines prominent in CLAUDE.md
- Remove redundant commit format section

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-27 07:53:22 +05:30
Laurens Priem
ddd1d5ac86 [mob][photos] Similar images UX changes (#6981)
## Description

Similar images UX changes

## Tests

Tested in debug mode on my pixel phone.
2025-08-26 23:50:33 +05:30
Laurens Priem
26845a502e [mob][photos] Use load instead of view on index (#6980)
## Description

Use load instead of index
2025-08-26 23:48:59 +05:30
laurenspriem
21aac29020 format count properly 2025-08-26 23:48:31 +05:30
laurenspriem
c1ff02df14 Always select all on tab change 2025-08-26 23:39:21 +05:30
laurenspriem
e4927c4022 Merge branch 'main' into similar_ux_changes 2025-08-26 23:30:32 +05:30
laurenspriem
4fd797338b Empty state 2025-08-26 23:26:51 +05:30
laurenspriem
eca0e5943d tab button look 2025-08-26 23:24:37 +05:30
laurenspriem
56cc7309a5 Show progress only for multiple albums symlinking 2025-08-26 23:05:04 +05:30
laurenspriem
b740d1af05 Show modal on 100+ deleted files only 2025-08-26 23:01:30 +05:30
laurenspriem
6d21b73367 faster select 2025-08-26 22:59:30 +05:30
laurenspriem
a5704eef25 Use load instead of view on index 2025-08-26 22:46:33 +05:30
laurenspriem
7e83682686 tiny margin in threshold 2025-08-26 22:42:04 +05:30
laurenspriem
18d5aa61b0 Extract string 2025-08-26 22:34:59 +05:30
laurenspriem
7c2a719ba8 (un)select all 2025-08-26 22:32:11 +05:30
Manav Rathi
2a136ba087 fix(rust): Fix file counting logic in sync and export commands
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 21:46:52 +05:30
laurenspriem
47313a74ff Tab bar filter 2025-08-26 21:28:21 +05:30
Manav Rathi
3abb479fbf feat(rust): Add progress indicators for downloads
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 21:26:39 +05:30
Manav Rathi
7eda60a493 fix(rust): Fix incremental sync to properly track per-collection timestamps
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 21:15:11 +05:30
Manav Rathi
bb8c5caa8d feat(rust): Handle renamed files using public magic metadata
Check both public magic metadata (for edited names) and regular metadata
when determining file names during export and sync, matching Go CLI behavior

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 21:00:11 +05:30
Prateek Sunal
65a7a16298 [mob][photos] fixes (#6979)
## Description

- [x] Fix Spacing in Video streaming settings
- [x] Update copy in Video Streaming settings
- [x] Disable debug notifications for work manager in iOS

## Tests
2025-08-26 20:57:02 +05:30
Prateek Sunal
9251e4f5b6 fix: update spacing and remove cross icon from top right 2025-08-26 19:07:44 +05:30
Prateek Sunal
c4bc6abf83 fix: remove debug mode notification flag 2025-08-26 19:07:27 +05:30
Manav Rathi
0384819c01 Take 2 2025-08-26 18:06:05 +05:30
Manav Rathi
f55973367d feat(rust): Add retry logic and export filters
- Add configurable retry with exponential backoff for API calls
- Handle 429 and 5xx errors with automatic retries
- Add export filters for albums, shared, and hidden collections
- Fix formatting and clippy warnings to pass CI checks

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 17:54:57 +05:30
laurenspriem
3165289483 Remove header 2025-08-26 17:48:39 +05:30
laurenspriem
01aab41c25 Select all by default 2025-08-26 17:46:53 +05:30
laurenspriem
1826258161 Copy 2025-08-26 17:43:58 +05:30
laurenspriem
df5917060b Copy change 2025-08-26 17:40:20 +05:30
Prateek Sunal
b5aa05cc1b [mob][photos] merge migration scripts (#6974)
## Description

Fixes #6923

## Tests
2025-08-26 17:16:10 +05:30
Prateek Sunal
cd865992f2 chore: directly use database 2025-08-26 17:02:21 +05:30
Prateek Sunal
370c0ab54a fix: merge migration scripts 2025-08-26 16:02:15 +05:30
Manav Rathi
699794226f fix(rust): Fix sync command file downloads
- Handle non-interactive mode in account add command
- Fix cross-filesystem file move issue by using copy+delete instead of rename
- Successfully tested downloading files from local server

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 16:00:15 +05:30
Manav Rathi
dee68acfc3 docs(rust): Reduce verbosity
Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 15:31:06 +05:30
eYdr1en
0bd5452837 Merge remote-tracking branch 'upstream/main' into touch-id 2025-08-26 11:07:30 +02:00
Neeraj
923f2484fb [auth] Fix missing token (#6971)
## Description
When using Auth without backup, it was giving a error `Offline key is
missing`

**Reason**: During the `init` of `BaseConfiguration` if the `tokenKey`
is not set, we clear all the keys in the secure storage, and in this
process the `offlineAuthSecretKey` was also getting cleared

**Fix** Fixed by skipping the deletion of `offlineAuthSecretKey`  

## Tests
[Test Video](https://wormhole.app/qz3mol#Dlhr0NRpVQVQsrid2X-quA)
2025-08-26 13:02:43 +05:30
AmanRajSinghMourya
37928cd2c6 Code refractoring 2025-08-26 12:40:03 +05:30
AmanRajSinghMourya
fc32ba97c1 Refactor BaseConfiguration to ensure preserved keys are not deleted 2025-08-26 12:26:01 +05:30
AmanRajSinghMourya
e49084867e Revert "Refactor BaseConfiguration to preserve offlineAuthSecretKey during logout"
This reverts commit 5b5f563d47.
2025-08-26 12:21:00 +05:30
Manav Rathi
e53ddb8b51 refactor(rust): Remove backward compatibility code
Since the CLI hasn't been released yet, we don't need to maintain
backward compatibility. This commit removes unnecessary compatibility
code to simplify the codebase.

Changes:
- Remove id field from Account struct (use user_id directly)
- Remove update_file_local_path legacy wrapper method
- Use mark_file_synced directly instead of the wrapper
- Update all references from account.id to account.user_id

This results in cleaner, more maintainable code without unnecessary
compatibility layers.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 10:12:26 +05:30
Laurens Priem
a046748ded [mob][photos] Minor fixes and changes (#6969)
## Description

Minor fixes and changes based on testing.

## Tests

Tested in debug mode on my pixel phone.
2025-08-26 10:08:42 +05:30
laurenspriem
047d708ef1 Merge branch 'main' into fix_modal 2025-08-26 10:08:18 +05:30
Manav Rathi
95d167878e refactor(rust): Eliminate redundant primary keys using global uniqueness
Since user_id is globally unique in Ente's system (like collection_id and
file_id), we can eliminate artificial primary keys and use the actual IDs
directly. This simplifies the schema and reduces redundancy.

Changes:
- Use (user_id, app) composite primary key in accounts table
- Use (user_id, app) composite primary key in secrets table
- Remove account_id references, use user_id directly
- Update collections table to use owner field (user_id)
- Update files table to use owner_id field (user_id)
- Remove account_id from album_files table
- Update sync_state table to use (user_id, app) primary key
- Update all storage methods to use new schema
- Update commands to pass correct parameters to storage methods
- Update indices for better query performance

This aligns with Ente's API design where these IDs are guaranteed to be
globally unique, eliminating the need for artificial primary keys.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 10:04:24 +05:30
AmanRajSinghMourya
5b5f563d47 Refactor BaseConfiguration to preserve offlineAuthSecretKey during logout 2025-08-26 10:02:19 +05:30
Ashil
2b60ad3748 [mob][packages] Organize imports (#6968) 2025-08-26 10:01:40 +05:30
Neeraj
1f70043c83 [mob][photos] video streaming settings & create/recreate stream (#6923) 2025-08-26 10:00:08 +05:30
laurenspriem
7ce6f6a346 Check symlink permissions 2025-08-26 09:55:17 +05:30
Manav Rathi
653fc47aed fix(rust): Fix clippy warning for collapsible if statement
Collapsed nested if statement in sync.rs to satisfy clippy's
collapsible-if lint rule. This change is required for CI to pass
with the updated Rust version.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 09:43:21 +05:30
ashilkn
03814bff0c Organize imports 2025-08-26 09:34:00 +05:30
Manav Rathi
34325691e7 refactor(rust): Use collection_id and file_id as primary keys
Since collection_id and file_id are globally unique across all users in
Ente's API, we can use them directly as primary keys instead of creating
artificial auto-increment IDs. This simplifies the schema and reduces
redundancy.

Changes:
- Use collection_id as primary key in collections table
- Use file_id as primary key in files table
- Use composite primary key (album_id, file_id) in album_files table
- Update all related SQL queries to match new schema
- Add appropriate foreign key constraints
- Optimize indices for the new structure

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 09:30:05 +05:30
laurenspriem
4c63a0ff13 Copy changes 2025-08-26 09:21:59 +05:30
Manav Rathi
e474114e22 fix(rust): Fix clippy warnings and improve CI documentation
- Fix collapsible if statement warnings in sync.rs and files.rs
- Update CLAUDE.md with clearer CI requirements
- Remove misleading auto-fix command that doesn't catch all issues
- Emphasize that ALL checks must pass before committing

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 09:14:33 +05:30
laurenspriem
93552fb872 vectorDB flag check ML enabled 2025-08-26 09:05:06 +05:30
laurenspriem
1b61becdcf Fix modal on group-level delete 2025-08-26 08:37:16 +05:30
Manav Rathi
80c07d36a9 feat(rust): Complete sync command with file downloads
- Integrated DownloadManager with sync command for actual file downloads
- Implemented proper sync state tracking using is_synced_locally flag
- Fixed database persistence by preserving sync state during updates
- Added proper collection key decryption for file downloads
- Files are only downloaded once and marked as synced
- Cleaned up schema - removed migrations since this is new code
- Fixed deserialization issues with RemoteFile thumbnail field
- Added proper error handling for missing collection keys

The sync command now:
1. Fetches metadata for collections and files
2. Downloads files that haven't been synced yet
3. Marks files as synced to avoid re-downloading
4. Properly handles existing files on disk

This matches the Go CLI's approach of using a synced flag rather than
checking file existence on every sync.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 06:27:09 +05:30
Manav Rathi
8581742a73 feat(rust): Integrate DownloadManager with sync command
- Added local_path column to files table for tracking downloaded files
- Implemented get_pending_downloads() to find files without local_path
- Integrated DownloadManager into sync command for full file downloads
- Added collection key decryption for file downloads
- Generate proper export paths with date/album structure
- Track successful downloads and update database with local paths
- Added migration to add local_path column to existing databases

The sync command now supports full file downloads (not just metadata).
Files are downloaded to the export directory with proper organization.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 05:55:03 +05:30
Manav Rathi
042dae8790 fix(rust): Apply cargo fmt and clippy fixes
- Fixed formatting issues in sync/engine.rs
- Added #[allow(dead_code)] for unused storage field in DownloadManager
- Replaced manual clamp with .clamp() method

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 05:41:33 +05:30
Prateek Sunal
0499cad3c9 chore: ignore generated mocks file 2025-08-26 04:48:32 +05:30
Prateek Sunal
79752ef4b8 chore: update file name 2025-08-26 04:35:51 +05:30
Prateek Sunal
c1bd6d3fdb Merge branch 'taking-streaming-oob' of https://github.com/ente-io/ente into taking-streaming-oob 2025-08-26 04:35:30 +05:30
Prateek Sunal
621423d9a4 fix: refactor code 2025-08-26 04:35:27 +05:30
Prateek Sunal
edb11c89ba fix: update mobile/apps/photos/lib/ui/settings/streaming/video_streaming_settings_page.dart
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-26 04:31:44 +05:30
Prateek Sunal
adb71fe09c fix: update mobile/apps/photos/lib/services/video_preview_service.dart
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-26 04:31:12 +05:30
Prateek Sunal
c20cee2406 feat: state update event, tests, total logic 2025-08-26 04:27:38 +05:30
Prateek Sunal
dcfad86c47 fix: ensure preview ids are present 2025-08-25 23:32:57 +05:30
Prateek Sunal
0a2bff67bf fix: delete file from upload locks db if not found 2025-08-25 23:31:51 +05:30
Prateek Sunal
7aaa689cfb fix: simplify logic 2025-08-25 23:26:34 +05:30
Prateek Sunal
ad2a0ce897 refactor: simplify StreamingStatus handling in VideoPreviewService 2025-08-25 23:22:39 +05:30
Prateek Sunal
d99615b24f fix: remove skipped 2025-08-25 22:16:22 +05:30
Prateek Sunal
09cc48ae55 fix: add comments 2025-08-25 21:50:07 +05:30
Prateek Sunal
6ab2223a80 fix: mostly all review comments 2025-08-25 21:42:40 +05:30
Prateek Sunal
6fd86162e0 Merge remote-tracking branch 'origin/main' into taking-streaming-oob 2025-08-25 20:58:09 +05:30
Prateek Sunal
707e8dbfcf fix: show processing status, fix when to not show popup buttons, update getFiles logic 2025-08-25 20:57:18 +05:30
Prateek Sunal
5869bec781 feat: create and recreate stream buttons 2025-08-25 20:11:08 +05:30
Laurens Priem
e311a8bb32 [mob][photos] Similar images UI (#6963)
## Description

Minor fixes and UI changes.

## Tests

Tested in debug mode on my pixel phone.
2025-08-25 16:55:29 +05:30
laurenspriem
547ccfceca Use shared preferences for tracking migration 2025-08-25 16:53:33 +05:30
Keerthana
3a1917949b [mobile/photos] New translations (#6955)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-app)
2025-08-25 13:30:03 +05:30
Keerthana
3a1ce3258e [auth] New translations (#6956)
New translations from
[Crowdin](https://crowdin.com/project/ente-authenticator-app)
2025-08-25 13:10:39 +05:30
Prateek Sunal
13b2542bea fix: update logic 2025-08-25 12:02:56 +05:30
Prateek Sunal
6db3741a3b Merge remote-tracking branch 'origin/main' into taking-streaming-oob 2025-08-25 11:48:49 +05:30
laurenspriem
ce17eccd68 Fix delete issue 2025-08-25 11:11:57 +05:30
Ashil
95dc683088 Update internal change log (#6959) 2025-08-25 11:11:28 +05:30
Neeraj
cf9d5f72f7 [mob] Fix query for duplicate cleanup (#6962)
## Description
Only consider owned files
## Tests
2025-08-25 11:10:36 +05:30
Neeraj Gupta
3096e1550a Merge remote-tracking branch 'origin/main' into fixQuery 2025-08-25 11:03:45 +05:30
Neeraj Gupta
1b39435735 Fix query 2025-08-25 11:02:25 +05:30
laurenspriem
8f3d8505bb Better UI when selection is not possible 2025-08-25 11:02:03 +05:30
Manav Rathi
47e8aafe25 [desktop] Update changelog (#6960) 2025-08-25 10:50:10 +05:30
Neeraj Gupta
bc6506cb10 Fix link for locker 2025-08-25 10:50:05 +05:30
Manav Rathi
edf32d065e [desktop] Update changelog 2025-08-25 10:47:43 +05:30
ashilkn
1fa6a0c3b9 Update internal change log 2025-08-25 10:41:16 +05:30
Neeraj Gupta
f2a26ba391 Minor refactor 2025-08-25 10:35:47 +05:30
Manav Rathi
2388989dd0 [web] Enable Czech (#6958) 2025-08-25 10:35:06 +05:30
laurenspriem
9e392277b1 Fix initState issue 2025-08-25 10:31:41 +05:30
Neeraj
4609c375db Revert "[auth] Add smaller Activision icon" (#6953)
Reverts ente-io/ente#6950

I rushed a bit, sorry. The PR wasn't meant to be merged yet (if ever)
and it won't work right now anyway. It was meant to create conversation
on the topic and then possibly merged and there may be concerns to this
as a company may not want their logo/wordmark altered but I'm not well
versed in this topic (idk maybe I'm overthinking this).

Discussion: #6951
2025-08-25 10:31:20 +05:30
Manav Rathi
839c62ea72 [web] Enable Czech 2025-08-25 10:28:23 +05:30
Neeraj
dceef49f33 [mob][photos] Pre-cache thumbnails fetched from LRU cache to Flutter's ImageCache for faster rendering (#6957)
## Description

In Gallery, even if thumbnails are stored in LRU cache, there was a
delay in rendering thumbnails when scrolling fast enough. Pre-caching
these thumbnails to flutter's `ImageCache` right before they're rendered
has made the rendering fast enough for seamless UX.

#### Before:


https://github.com/user-attachments/assets/c47958fb-fbda-4e1f-9ce7-26b51ca87938

#### After:


https://github.com/user-attachments/assets/cbaf4427-f52f-4544-a0c2-820eb2b43953
2025-08-25 10:18:05 +05:30
laurenspriem
acbdc3111a Remove use of withOpacity 2025-08-25 10:15:49 +05:30
laurenspriem
98b91a6935 Remove duplicate string 2025-08-25 10:11:07 +05:30
laurenspriem
e1640e67d4 Extract strings 2025-08-25 10:09:03 +05:30
laurenspriem
e875758419 Simplify sort options 2025-08-25 09:48:33 +05:30
Manav Rathi
214b120472 [web] New translations (#6954)
New translations from
[Crowdin](https://crowdin.com/project/ente-photos-web)
2025-08-25 09:43:37 +05:30
ashilkn
f139e0a098 Fix flickering of GalleryFileWidget on hero animation after closing it's open full view 2025-08-25 09:21:57 +05:30
laurenspriem
e3c9a61887 Align 2025-08-25 09:20:19 +05:30
ashilkn
0da3dc5084 Skip clearing flutter image cache since default (current) limit is 100MB and the threshold to clear is 250MB 2025-08-25 09:20:08 +05:30
ashilkn
a856a82249 Refactor 2025-08-25 09:18:29 +05:30
ashilkn
fbdec00a62 Improve lru cache thumbnail rendering speed when scrolling gallery by precaching it it flutter's image cache 2025-08-25 09:16:59 +05:30
Crowdin Bot
6a7f980a0d New Crowdin translations by GitHub Action 2025-08-25 01:18:00 +00:00
Crowdin Bot
10a855fe27 New Crowdin translations by GitHub Action 2025-08-25 01:05:02 +00:00
Crowdin Bot
b4f8a2b27c New Crowdin translations by GitHub Action 2025-08-25 00:39:57 +00:00
dnred
89489b4d7c Revert "[auth] Add smaller Activision icon" 2025-08-24 23:48:21 +02:00
Aman Raj Singh Mourya
50296f8dfa [auth] Add smaller Activision icon (#6950)
## Description

The current Activision icon is too wide and small to be nicely displayed
in Auth so this PR adds a smaller one, just like the favicon on
Activision's [website](https://activision.com).

I know the Activision icon is pulled from simple-icons and I don't want
to get rid of that, just add an option for a smaller one, but I see that
the smaller Allegro icon is also added but it isn't displayed in the
icon picker and the icon from simple-icons takes precedence so you'd
have to figure this out.

## Tests

I haven't tested this.
2025-08-24 23:15:44 +05:30
dnred
f69cec864b Rename activision2.svg to activision.svg 2025-08-24 16:42:49 +02:00
dnred
73d5d33fc5 Update custom-icons.json 2025-08-24 16:25:47 +02:00
dnred
4d8ea12ddd Add logo 2025-08-24 16:17:25 +02:00
Prateek Sunal
7beb267ba7 chore: remove unused import 2025-08-24 02:15:09 +05:30
Prateek Sunal
7e13ef3537 chore: remove formatting for files_db 2025-08-24 02:10:06 +05:30
Prateek Sunal
47edca5bf5 chore: fix formatting 2025-08-24 02:08:00 +05:30
Prateek Sunal
925ba10b15 chore: revert remote_sync formatting 2025-08-24 02:06:28 +05:30
Prateek Sunal
db2d0bb7e9 fix: remove formatting from ml_service 2025-08-24 02:04:09 +05:30
Prateek Sunal
f3a2b2af0c fix: include it in if loop 2025-08-24 02:02:47 +05:30
Prateek Sunal
967e88f88d Merge remote-tracking branch 'origin/main' into taking-streaming-oob 2025-08-24 02:00:33 +05:30
Prateek Sunal
b44734a493 fix: add streaming static image 2025-08-24 01:59:23 +05:30
Manav Rathi
6478b08a19 [docs] Minor improvements to self-hosted docs (#6936)
## Description
- Small correction on the self-hosted docker exec command.
- Added tip on how to install Ente CLI.

In spirit of starting with a small PR :p
2025-08-23 21:54:47 +05:30
Laurens Priem
314e81565b Fix linter issues (#6939)
## Description

Linter was failing because it didn't first run
`flutter_rust_bridge_codegen generate` to generate the dart bindings to
rust code.
2025-08-23 20:34:02 +05:30
laurenspriem
f95e20d00f Consistent tap behaviour 2025-08-23 17:35:12 +05:30
laurenspriem
35a04d6e7e Don't unnecessarily sort 2025-08-23 17:34:53 +05:30
laurenspriem
403264d2c9 Fix linter issues 2025-08-23 17:20:22 +05:30
Manav Rathi
84f5a5ac3d feat(rust): Add sync command and fix database path
- Add new `sync` command to fetch collections and file metadata
- Change config directory from ~/.ente/ to ~/.config/ente-cli/ to avoid conflicts with Go CLI
- Fix sync engine to use correct API endpoints (/collections/v2/diff instead of /diff)
- Implement per-collection file syncing matching Go CLI behavior
- Fix foreign key constraints in database schema
- Add metadata-only and full sync options
- Store database path in Storage struct for creating new instances
- Successfully tested with real account: syncs 5 files and exports correctly

The sync command now properly fetches all collections and files from the API,
storing them in SQLite for offline access and incremental sync support.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 16:47:08 +05:30
Toby
6b06a4c388 Add instructions on how to install Ente CLI 2025-08-23 13:13:08 +02:00
Toby
678bce89b2 Add small corrections to docker commands 2025-08-23 13:06:34 +02:00
Manav Rathi
a00fc0b1be fix(rust): Remove sensitive information from logs and docs
Security improvements:
- Remove all debug logs that output tokens, keys, or credentials
- Remove email addresses from debug output
- Remove encrypted keys and nonces from logs
- Remove specific account references from documentation
- Add security guidelines to CLAUDE.md

No sensitive information (PII, credentials, tokens) should be logged
even in debug mode. Updated guidelines to prevent future occurrences.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 15:06:28 +05:30
Manav Rathi
f5347e7436 docs(rust): Update conversion plan with completed features
- Mark streaming XChaCha20-Poly1305 implementation as complete
- Document successful export functionality with all decryption working
- Update testing status with successful real account exports
- Add recent achievements section highlighting key milestones
- Update feature parity progress checklist
- Document what components are complete vs remaining

The export functionality is now fully working with proper decryption
of collections, files, and metadata. Updated PR description as well.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 14:26:09 +05:30
Manav Rathi
3f1d574d0c feat(rust): Add progress indicators to export
- Show progress for each exported file with count
- Improve export summary with emojis and better formatting
- Add contextual success messages based on export results
- Make export output more user-friendly

The export now provides clear feedback during the process and
a helpful summary at the end.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 14:14:59 +05:30
Manav Rathi
891b68c0f4 fix(rust): Add chunked decryption for large files
- Implement chunked streaming decryption matching Go's 4MB buffer size
- Update file decryption to use decrypt_file_data instead of decrypt_stream
- Successfully tested with 33MB RAW image file
- All test files now decrypt correctly

Large files are now properly handled with chunked decryption, preventing
memory issues and matching the Go implementation's behavior.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 14:12:06 +05:30
Manav Rathi
f050c6f9d7 feat(rust): Implement streaming XChaCha20-Poly1305 decryption
- Add streaming cipher module using libsodium's secretstream API
- Update file and metadata decryption to use streaming XChaCha20-Poly1305
- Fix decryption issues - files now properly decrypt
- Successfully tested with real account - exports working for smaller files

The export now correctly decrypts files using the same streaming cipher
as the Go implementation. Large files may need chunked decryption support.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 14:01:59 +05:30
Manav Rathi
2de67b619f feat(rust): Add metadata decryption for original filenames
- Create metadata module with FileMetadata struct
- Decrypt file metadata to extract original filename
- Use original filename in export path generation
- Add proper file type detection from metadata
- Implement filename sanitization for filesystem safety

Files are now exported with their original names instead of generic IDs.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 13:25:38 +05:30
Manav Rathi
828dde5ca7 feat(rust): Implement file decryption in export command
- Add ChaCha20-Poly1305 decryption for downloaded files
- Decrypt file keys using master key
- Extract nonce from encrypted file data
- Add basic filename generation with extension detection
- Comment out sync modules temporarily due to model mismatches

The export command now properly decrypts files instead of saving them encrypted.
Next steps: extract original filenames from decrypted metadata.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 13:03:46 +05:30
Manav Rathi
2526c69896 docs(rust): Update pre-commit checklist to match CI configuration
Ensure clippy commands use --all-targets --all-features flags to match
the CI environment exactly. This prevents CI failures from warnings that
weren't caught locally.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 12:09:42 +05:30
Manav Rathi
6e64a2067f fix(rust): Resolve all clippy warnings for CI compliance
- Fix lifetime elision warnings in storage/mod.rs by adding explicit lifetimes
- Collapse nested if-let statements in export.rs using let-chains
- Code now passes: cargo clippy --all-targets --all-features

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 09:58:29 +05:30
Manav Rathi
ab4792518f docs(rust): Add mandatory pre-commit checklist to CLAUDE.md
Add explicit pre-commit commands that must be run before every commit
to ensure CI passes. These commands simulate the CI environment locally.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 09:29:28 +05:30
Manav Rathi
d4ae8d63fc fix(rust): Fix linting and formatting issues for CI
- Applied cargo fmt to ensure consistent formatting
- Fixed all clippy warnings (uninlined_format_args)
- Code now passes all CI checks with RUSTFLAGS="-D warnings"

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 09:01:56 +05:30
Manav Rathi
618753cb1a feat(rust): Implement export command with collection-based file fetching
- Fix token encoding to use base64 URL with padding (matching Go implementation)
- Add export command that iterates through collections and fetches files
- Update API models to handle actual server response field names (ownerID vs ownerId)
- Fix file download URLs for local/dev environments
- Implement proper directory structure creation (YYYY/MM-Month format)
- Add collection attributes and public URL models for complete API compatibility
- Successfully exports encrypted files from both local and production endpoints

The export command now:
- Fetches all collections for an account
- Iterates through each collection to get files
- Downloads encrypted files and saves them to the export directory
- Skips already downloaded files to support incremental exports

Note: Files are still encrypted; decryption will be implemented in a future commit.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 21:13:52 +05:30
Manav Rathi
f84bd20bbf feat(rust): Store API endpoint per account for better environment isolation
- Add endpoint field to accounts database table with default to production API
- Update Account model to include endpoint field
- Add --endpoint flag to account add command only
- Remove ENTE_ENDPOINT environment variable support
- Update account list to display endpoints in readable format
- Each account now maintains its own endpoint, preventing confusion between test and production environments

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 20:25:39 +05:30
Prateek Sunal
8b708228be fix: add different ui for enabling it 2025-08-22 19:04:16 +05:30
Manav Rathi
6ae7aa70d6 fix(rust): Fix FFI type cast and clippy warnings for CI
- Use std::ffi::c_char for libsodium FFI context parameter cast
- Fix all clippy warnings to pass CI with RUSTFLAGS="-D warnings"
- Update CLAUDE.md with FFI casting guidance for future development

This ensures the code passes all CI checks including the stricter
clippy settings used in GitHub Actions.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 18:49:24 +05:30
Manav Rathi
48757af5d0 fix(rust): Fix SRP authentication implementation
Fixed issues preventing successful authentication:
- Corrected Argon2 memory limit handling (API sends bytes, not KB)
- Replaced Blake2b with crypto_kdf_derive_from_key for login subkey derivation
- Fixed serde field names to match API expectations (srpUserID, sessionID)
- Added non-interactive mode for CLI testing
- Added support for ENTE_ENDPOINT environment variable

The implementation now matches the web client's key derivation exactly,
enabling successful authentication with both local and production servers.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 18:23:16 +05:30
Manav Rathi
cd20a98850 feat(rust): Implement account management commands
Add comprehensive account management functionality with secure SRP authentication.
This enables users to add, list, update, and manage multiple Ente accounts
for photos, locker, and auth apps.

Key features:
- Complete account add flow with SRP authentication
- Two-factor authentication support (TOTP)
- Secure key decryption and storage
- Multi-account support with per-app configuration
- Account list and update commands
- Export directory management
- Interactive CLI prompts with dialoguer

The implementation integrates with the API client for authentication and
securely stores account credentials in SQLite.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 13:57:29 +05:30
Manav Rathi
9ac9e6bd26 feat(rust): Implement comprehensive API client for Ente services
Add complete API client implementation with authentication, file operations,
and collection management. This enables the Rust CLI to interact with Ente
servers for photo backup and sync operations.

Key features:
- Multi-account token management with secure storage
- SRP authentication flow matching Go implementation
- Retry logic with exponential backoff for network resilience
- Full API coverage: auth, collections, files, trash, user details
- Request/response models for all API endpoints
- Separate download client for large file transfers
- Smart CDN routing for production file downloads

The implementation follows the conversion plan and maintains compatibility
with the existing Go CLI API patterns.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 12:14:05 +05:30
Manav Rathi
0b640c9062 docs(rust): Enhance CLAUDE.md with comprehensive codebase guidance
Add detailed development commands, architecture overview, and module
descriptions to help future Claude instances understand the codebase
structure and development workflow.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 10:38:57 +05:30
Manav Rathi
2d87aba165 docs(rust): Add comprehensive conversion plan
- Document current implementation status
- Detail API client implementation steps
- List all remaining components with specifications
- Include testing strategy and migration notes
- Provide file structure reference for navigation
- Add implementation guidelines and environment variables

This plan enables any developer to understand the project state
and continue the conversion work from the current point.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 10:03:01 +05:30
Manav Rathi
7dffdfaecf feat(rust): Implement SQLite storage layer
- Replace sled with SQLite for better reliability and tooling
- Create schema with tables for accounts, secrets, collections, files, and sync state
- Implement account storage with multi-account support
- Add configuration and sync state management
- Support for storing encrypted credentials separately
- Add indices for performance optimization

SQLite provides ACID transactions, better debugging tools, and a proven
track record for reliability with user data.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 09:50:59 +05:30
Manav Rathi
a4da7b5555 main safeguard 2025-08-22 09:26:55 +05:30
Prateek Sunal
42d31a73a3 fix: update localization & fix lint 2025-08-21 23:32:45 +05:30
Prateek Sunal
946d2ae522 fix: remove unnecessary blank line in analysis_options.yaml + circular button 2025-08-21 22:51:01 +05:30
Prateek Sunal
8e9eb50783 fix: downgrade Dart SDK version to 3.7.2 2025-08-21 22:50:24 +05:30
Prateek Sunal
af3bc7757f fix: downgrade dart sdk again 2025-08-21 22:50:04 +05:30
Prateek Sunal
eda1d05216 fix: revert dart sdk 2025-08-21 22:49:49 +05:30
Prateek Sunal
b58e0f8331 fix: remove analysis options for now 2025-08-21 22:49:37 +05:30
Prateek Sunal
6dcf53650d fix: re-add captioned text 2025-08-21 22:48:42 +05:30
Prateek Sunal
bff53d9081 chore: add back space 2025-08-21 22:47:42 +05:30
Prateek Sunal
f3306e14c7 fix: revert 2025-08-21 22:47:30 +05:30
Prateek Sunal
b5c075bac4 fix: add space at end 2025-08-21 22:45:38 +05:30
Prateek Sunal
241d21c2aa fix: revert things 2025-08-21 22:44:34 +05:30
Prateek Sunal
789d77747c fix: update other contact page 2025-08-21 22:35:24 +05:30
Prateek Sunal
35050aa32f fix: delete generated intl_utils locale 2025-08-21 22:33:23 +05:30
Prateek Sunal
40e6bd9fae Merge remote-tracking branch 'origin/main' into taking-streaming-oob 2025-08-21 22:32:41 +05:30
Prateek Sunal
b317df2000 Merge branch 'main' into taking-streaming-oob 2025-08-21 18:11:42 +05:30
Prateek Sunal
2e706228ee feat: separate settings page improvements 2025-08-21 17:22:57 +05:30
Manav Rathi
85b766b5d0 Safeguard 2025-08-21 16:31:35 +05:30
Manav Rathi
62f715d3c1 fix(rust): Use std::ffi::c_char for FFI type casting
- Replace libc::c_char with std::ffi::c_char for password parameter
- Remove unnecessary libc dependency
- Use standard library FFI types (available since Rust 1.64)

This fixes the CI build error where libsodium expects *const c_char
for the password parameter in crypto_pwhash.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-21 16:27:50 +05:30
Manav Rathi
e35ae86fa5 Ask it to run cargo fmt etc
For the current session cc was able to use that instruction to figure out all the linters etc to run. If that doesn't work in future sessions, we can use a longer instruction, something like what it itself suggested

    ## CI/CD Requirements
    - Must pass `cargo fmt --check`
    - Must pass `cargo clippy --all-targets --all-features`
    - Must pass `RUSTFLAGS="-D warnings" cargo build`
    - Fix all formatting before committing
    - Address all clippy warnings
    - Use `#![allow(dead_code)]` during development for unused code

    ## Code Quality
    - Run `cargo fmt` before committing
    - Fix clippy warnings: remove unnecessary casts, use idiomatic Rust
    - Prefix unused variables with underscore
    - Remove unused imports
2025-08-21 12:33:14 +05:30
Manav Rathi
ea843eba7a fix(rust): Address cargo fmt and clippy warnings
- Fix code formatting with cargo fmt
- Remove unnecessary type casts
- Use range contains instead of manual comparison
- Prefix unused variables with underscore
- Remove unused imports
- Add allow(dead_code) for development phase

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-21 12:24:04 +05:30
Manav Rathi
b845f4d893 Keep co-author but remove the self promo link
Typical Claude Code commit message:
feat: implement user authentication

- Added login endpoint
- Implemented JWT tokens
- Created middleware

Created with Claude Code: https://claude.ai/code  # <-- The promotional link
Co-authored-by: Claude <claude@anthropic.com>      # <-- The co-author line

This memory is to ask claude to keep the co-author line but remove the self promo from the commit message it creates.
2025-08-21 06:51:22 +05:30
Manav Rathi
8ea36acb7a feat(rust): Initialize Rust CLI foundation with libsodium crypto
- Set up project structure mirroring Go CLI architecture
- Add dependencies with libsodium-sys-stable for all crypto operations
- Implement core crypto module with Argon2, ChaCha20-Poly1305, and Blake2b
- Create data models for accounts, files, and collections
- Set up Clap-based CLI framework with account, export, and version commands
- Add error handling with thiserror
- Configure for static linking to create standalone binaries

This establishes the foundation for converting the Ente CLI from Go to Rust,
with a focus on maintaining compatibility with existing libsodium-based crypto.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-21 06:50:23 +05:30
Prateek Sunal
fe40185889 fix: redirect video streaming to setting page 2025-08-20 14:43:54 +05:30
Prateek Sunal
6bb4428a8a chore: update generated translations 2025-08-20 14:41:57 +05:30
Prateek Sunal
0881685915 feat: add video streaming settings page 2025-08-20 14:34:16 +05:30
Prateek Sunal
3e13932d03 chore: format files and update analysis file 2025-08-20 14:33:35 +05:30
eYdr1en
279df8ff57 fixes lint error 2025-08-13 12:52:13 +02:00
Adrián Horváth
d83994c692 Update mobile/apps/auth/lib/ui/tools/lock_screen.dart
Co-authored-by: Prateek Sunal <prtksunal@gmail.com>
2025-08-13 12:37:11 +02:00
eYdr1en
be506bdad1 fixes macos touch id lock 2025-08-08 17:22:12 +02:00
217 changed files with 20193 additions and 4918 deletions

View File

@@ -27,6 +27,38 @@ jobs:
with:
submodules: recursive
- name: Free up disk space
run: |
echo "Initial disk usage:"
df -h /
# Get available space in KB
INITIAL=$(df / | awk 'NR==2 {print $4}')
echo -e "\n=== Removing .NET SDK (~20-25GB) ==="
BEFORE=$(df / | awk 'NR==2 {print $4}')
START=$(date +%s)
sudo rm -rf /usr/share/dotnet
END=$(date +%s)
AFTER=$(df / | awk 'NR==2 {print $4}')
FREED=$(( (AFTER - BEFORE) / 1048576 )) # Convert KB to GB
echo "Time: $((END-START))s | Freed: ${FREED}GB"
echo -e "\n=== Removing cached tools (~5-10GB) ==="
BEFORE=$(df / | awk 'NR==2 {print $4}')
START=$(date +%s)
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
END=$(date +%s)
AFTER=$(df / | awk 'NR==2 {print $4}')
FREED=$(( (AFTER - BEFORE) / 1048576 ))
echo "Time: $((END-START))s | Freed: ${FREED}GB"
echo -e "\n=== Final Summary ==="
FINAL=$(df / | awk 'NR==2 {print $4}')
TOTAL_FREED=$(( (FINAL - INITIAL) / 1048576 ))
echo "Total space freed: ${TOTAL_FREED}GB"
echo "Final disk usage:"
df -h /
- name: Setup JDK 17
uses: actions/setup-java@v1
with:
@@ -39,11 +71,6 @@ jobs:
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install Rust ${{ env.RUST_VERSION }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_VERSION }}
- name: Install Flutter Rust Bridge
run: cargo install flutter_rust_bridge_codegen

View File

@@ -1,77 +0,0 @@
name: "Old Internal release (photos)"
on:
workflow_dispatch: # Allow manually running the action
env:
FLUTTER_VERSION: "3.32.8"
RUST_VERSION: "1.85.1"
permissions:
contents: write
jobs:
build:
runs-on: ubuntu-latest
defaults:
run:
working-directory: mobile/apps/photos
steps:
- name: Checkout code and submodules
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup JDK 17
uses: actions/setup-java@v1
with:
java-version: 17
- name: Install Flutter ${{ env.FLUTTER_VERSION }}
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install Rust ${{ env.RUST_VERSION }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_VERSION }}
- name: Install Flutter Rust Bridge
run: cargo install flutter_rust_bridge_codegen
- name: Setup keys
uses: timheuer/base64-to-file@v1
with:
fileName: "keystore/ente_photos_key.jks"
encodedString: ${{ secrets.SIGNING_KEY_PHOTOS }}
- name: Build PlayStore AAB
run: |
flutter build appbundle --dart-define=cronetHttpNoPlay=true --release --flavor playstore
env:
SIGNING_KEY_PATH: "/home/runner/work/_temp/keystore/ente_photos_key.jks"
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS_PHOTOS }}
SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD_PHOTOS }}
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD_PHOTOS }}
- name: Upload AAB to PlayStore
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
packageName: io.ente.photos
releaseFiles: mobile/apps/photos/build/app/outputs/bundle/playstoreRelease/app-playstore-release.aab
track: internal
- name: Notify Discord
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.DISCORD_INTERNAL_RELEASE_WEBHOOK }}
nodetail: true
title: "🏆 Internal release Photos (Branch: ${{ github.ref_name }})"
description: "[Download](https://play.google.com/store/apps/details?id=io.ente.photos)"
color: 0x00ff00

View File

@@ -9,6 +9,7 @@ on:
env:
FLUTTER_VERSION: "3.32.8"
RUST_VERSION: "1.86.0"
permissions:
contents: read
@@ -31,7 +32,18 @@ jobs:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- run: flutter pub get
- name: Install Rust ${{ env.RUST_VERSION }}
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_VERSION }}
- name: Install Flutter Rust Bridge
run: cargo install flutter_rust_bridge_codegen
- name: Generate Rust bindings
run: flutter_rust_bridge_codegen generate
- run: flutter analyze --no-fatal-infos

View File

@@ -28,6 +28,38 @@ jobs:
with:
submodules: recursive
- name: Free up disk space
run: |
echo "Initial disk usage:"
df -h /
# Get available space in KB
INITIAL=$(df / | awk 'NR==2 {print $4}')
echo -e "\n=== Removing .NET SDK (~20-25GB) ==="
BEFORE=$(df / | awk 'NR==2 {print $4}')
START=$(date +%s)
sudo rm -rf /usr/share/dotnet
END=$(date +%s)
AFTER=$(df / | awk 'NR==2 {print $4}')
FREED=$(( (AFTER - BEFORE) / 1048576 )) # Convert KB to GB
echo "Time: $((END-START))s | Freed: ${FREED}GB"
echo -e "\n=== Removing cached tools (~5-10GB) ==="
BEFORE=$(df / | awk 'NR==2 {print $4}')
START=$(date +%s)
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
END=$(date +%s)
AFTER=$(df / | awk 'NR==2 {print $4}')
FREED=$(( (AFTER - BEFORE) / 1048576 ))
echo "Time: $((END-START))s | Freed: ${FREED}GB"
echo -e "\n=== Final Summary ==="
FINAL=$(df / | awk 'NR==2 {print $4}')
TOTAL_FREED=$(( (FINAL - INITIAL) / 1048576 ))
echo "Total space freed: ${TOTAL_FREED}GB"
echo "Final disk usage:"
df -h /
- name: Setup JDK 17
uses: actions/setup-java@v1
with:
@@ -40,6 +72,12 @@ jobs:
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install Flutter Rust Bridge
run: cargo install flutter_rust_bridge_codegen
- name: Generate Rust bindings
run: flutter_rust_bridge_codegen generate
- name: Setup keys
uses: timheuer/base64-to-file@v1
with:

65
CLAUDE.md Normal file
View File

@@ -0,0 +1,65 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Repository Overview
Ente is a monorepo containing end-to-end encrypted cloud storage applications (Photos and Auth), with clients for multiple platforms and a self-hostable backend server. The codebase uses end-to-end encryption for all user data, ensuring privacy and security.
## Common Development Commands
### Web Development
- **Run Photos app**: `cd web && yarn dev:photos` (port 3000)
- **Run Auth app**: `cd web && yarn dev:auth` (port 3003)
- **Build Photos**: `cd web && yarn build:photos`
- **Lint and typecheck**: `cd web && yarn lint`
- **Fix linting issues**: `cd web && yarn lint-fix`
### Mobile Development (Flutter)
- **Bootstrap monorepo**: `cd mobile && melos bootstrap`
- **Run Photos app**: `cd mobile && melos run:photos:apk`
- **Run Auth app**: `cd mobile && melos run:auth:apk`
- **Build Photos APK**: `cd mobile && melos build:photos:apk`
- **Clean all projects**: `cd mobile && melos clean:all`
### Desktop Development (Electron)
- **Run development**: `cd desktop && yarn dev`
- **Build quickly**: `cd desktop && yarn build:quick`
- **Full build**: `cd desktop && yarn build`
- **Lint**: `cd desktop && yarn lint`
### Server Development (Go)
- **Run locally**: `cd server && docker compose up --build`
- **API endpoint**: `http://localhost:8080`
- **Health check**: `curl http://localhost:8080/ping`
## Architecture
### Encryption Architecture
The system implements end-to-end encryption using:
- **Master Key**: Generated client-side, never leaves device unencrypted
- **Key Encryption Key**: Derived from user password using Argon2
- **Collection Keys**: Per-folder/album encryption keys
- **File Keys**: Individual encryption for each file
- Uses libsodium for all cryptographic operations
### Project Structure
- `web/apps/` - Next.js web applications (photos, auth, accounts, etc.)
- `mobile/apps/` - Flutter applications for iOS/Android
- `desktop/` - Electron desktop application
- `server/` - Go backend API (Museum)
- `cli/` - Command-line interface
- `docs/` - Documentation
- `infra/` - Infrastructure and deployment configs
### Key Technologies
- **Frontend**: Next.js, React, TypeScript
- **Mobile**: Flutter, Dart
- **Desktop**: Electron, TypeScript
- **Backend**: Go, PostgreSQL, Docker
- **Cryptography**: libsodium, end-to-end encryption
## Testing Approach
- Check for test scripts in package.json files
- Mobile tests can be run with Flutter's test command
- Server tests use Go's built-in testing framework

View File

@@ -2,6 +2,8 @@
## v1.7.15 (Unreleased)
- Custom domains.
- Support Czech translations.
- .
## v1.7.14

View File

@@ -8,6 +8,12 @@ description: Guide to configuring Ente CLI for Self Hosted Instance
If you are self-hosting, you can configure Ente CLI to export data & perform
basic administrative actions.
::: tip Installing Ente CLI
For instructions on installing the Ente CLI, see the [README available on Github](https://github.com/ente-io/ente/tree/main/cli/README.md).
:::
## Step 1: Configure endpoint
To do this, first configure the CLI to use your server's endpoint.

View File

@@ -22,8 +22,7 @@ can achieve this the following steps:
# Change the DB name and DB user name if you use different
# values.
# If using Docker
docker exec -it <postgres-ente-container-name>
docker exec -it <postgres-ente-container-name> sh
psql -U pguser -d ente_db
# Or when using psql directly

View File

@@ -46,7 +46,7 @@ If running Museum without Docker, the code should be visible in the terminal
# Change the DB name and DB user name if you use different
# values.
# If using Docker docker exec -it <postgres-ente-container-name>
# If using Docker docker exec -it <postgres-ente-container-name> sh
psql -U pguser -d ente_db
# Or when using psql directly

View File

@@ -1236,6 +1236,12 @@
"title": "Parqet",
"slug": "parqet"
},
{
"title": "Parallels",
"slug": "parallels",
"hex": "#E61E25",
"altNames": ["Parallels Desktop", "Parallels VM"]
},
{
"title": "Parsec"
},

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
<rect x="20" y="10" width="10" height="80" rx="5" fill="#E61E25"/>
<rect x="50" y="10" width="10" height="80" rx="5" fill="#E61E25"/>
</svg>

After

Width:  |  Height:  |  Size: 207 B

View File

@@ -111,6 +111,7 @@
"importAegisGuide": "Použijte možnost \"Export the vault\" v nastavení aplikace Aegis.",
"import2FasGuide": "Použijte možnost \"Settings->Backup -Export\" v 2FA.\n\nPokud je Vaše záloha šifrovaná, budete muset zadat heslo pro její odemčení",
"importLastpassGuide": "V nastavení aplikace Lastpass Authenticator vyberte možnost \"Transfer accounts\" a poté \"Export accounts to file\". Vygenerovaný soubor JSON následně nahrajte sem.",
"importProtonAuthGuide": "K exportu kódů použijte možnost „Exportovat“ v nastavení aplikace Proton Authenticator.",
"exportCodes": "Exportovat kódy",
"importLabel": "Importovat",
"importInstruction": "Vyberte, prosím, soubor obsahující seznam Vašich kódů v následujícím formátu",
@@ -124,6 +125,7 @@
"authToChangeYourEmail": "Pro změnu svého e-mailu se, prosím, ověřte",
"authToChangeYourPassword": "Pro změnu svého hesla se, prosím, ověřte",
"authToViewSecrets": "Pro zobrazení svých tajných údajů se musíte ověřit",
"authToInitiateSignIn": "Proveďte ověření a přihlaste se k zálohování.",
"ok": "Ok",
"cancel": "Zrušit",
"yes": "Ano",
@@ -171,6 +173,7 @@
"invalidQRCode": "Neplatný QR kód",
"noRecoveryKeyTitle": "Nemáte obnovovací klíč?",
"enterEmailHint": "Zadejte svou e-mailovou adresu",
"enterNewEmailHint": "Zadejte svou novou e-mailovou adresu",
"invalidEmailTitle": "Neplatná e-mailová adresa",
"invalidEmailMessage": "Prosím, zadejte platnou e-mailovou adresu.",
"deleteAccount": "Odstranit účet",
@@ -509,6 +512,19 @@
"supportEnte": "Podpořte <bold-green>ente</bold-green>",
"giveUsAStarOnGithub": "Dejte nám hvězdu na Githubu",
"free5GB": "5GB zdarma na <bold-green>ente</bold-green> Fotky",
"loginWithAuthAccount": "Přihlaste se pomocí svého účtu Auth",
"freeStorageOffer": "10% sleva na <bold-green>ente</bold-green> fotky",
"freeStorageOfferDescription": "Použijte kód \"AUTH\" pro získání 10% slevy na první rok"
"freeStorageOfferDescription": "Použijte kód \"AUTH\" pro získání 10% slevy na první rok",
"advanced": "Pokročilé",
"algorithm": "Algoritmus",
"type": "Typ",
"period": "Období",
"digits": "Digitální",
"importFromGallery": "Importovat z galerie",
"errorCouldNotReadImage": "Nelze přečíst vybraný obrazový soubor.",
"errorInvalidQRCode": "Neplatný QR kód",
"errorInvalidQRCodeBody": "Naskenovaný QR kód není platným účtem 2FA.",
"errorNoQRCode": "Nenalezen žádný QR kód",
"errorGenericTitle": "Došlo k chybě",
"errorGenericBody": "Při importu došlo k neočekávané chybě."
}

View File

@@ -111,6 +111,7 @@
"importAegisGuide": "Verwenden Sie die Option \"Tresor exportieren\" in den Einstellungen von Aegis.\n\nFalls Ihr Tresor verschlüsselt ist, müssen Sie das Passwort für den Tresor eingeben, um ihn zu entschlüsseln.",
"import2FasGuide": "Verwenden Sie unter \"Einstellungen → Backup\" die Option \"Exportieren\" in 2FAS.\n\nFalls Ihr Backup verschlüsselt ist, müssen Sie das Passwort eingeben, um das Backup zu entschlüsseln.",
"importLastpassGuide": "Verwenden Sie die Option \"Konten übertragen → Konten in Datei exportieren\" in den Lastpass Authenticator Einstellungen. \nImportieren Sie anschließend die heruntergeladene JSON-Datei.",
"importProtonAuthGuide": "Verwenden Sie die Option \"Exportieren\" in den Proton Authenticator Settings um Ihre Codes zu exportieren.",
"exportCodes": "Codes exportieren",
"importLabel": "Importieren",
"importInstruction": "Bitte wählen Sie eine Datei die Codes in folgendem Format beinhaltet",
@@ -519,5 +520,12 @@
"algorithm": "Algorithmus",
"type": "Typ",
"period": "Periode",
"digits": "Ziffern"
"digits": "Ziffern",
"importFromGallery": "Aus Galerie importieren",
"errorCouldNotReadImage": "Die ausgewählte Bild-Datei konnte nicht verarbeitet werden.",
"errorInvalidQRCode": "Ungültiger QR-Code",
"errorInvalidQRCodeBody": "Der gescannte QR-Code ist kein gültiges 2FA-Konto.",
"errorNoQRCode": "Kein QR-Code gefunden",
"errorGenericTitle": "Ein Fehler ist aufgetreten",
"errorGenericBody": "Beim Importieren ist ein unerwarteter Fehler aufgetreten."
}

View File

@@ -111,6 +111,7 @@
"importAegisGuide": "Utilisez l'option \"Exporter le coffre-fort\" dans les paramètres d'Aegis.\n\nSi votre coffre-fort est crypté, vous devrez saisir le mot de passe du coffre-fort pour déchiffrer le coffre-fort.",
"import2FasGuide": "Utilisez l'option \"Paramètres->Sauvegarde -Export\" dans 2FAS.\n\nSi votre sauvegarde est chiffrée, vous devrez entrer le mot de passe pour déchiffrer la sauvegarde",
"importLastpassGuide": "Utilisez l'option \"Transférer des comptes\" dans les paramètres de l'authentificateur Lastpass et appuyez sur \"Exporter des comptes vers un fichier\". Importez le JSON téléchargé.",
"importProtonAuthGuide": "Utilisez l'option \"Export\" dans les paramètres de Proton Authenticator pour exporter vos codes.",
"exportCodes": "Exporter les codes",
"importLabel": "Importer",
"importInstruction": "Veuillez sélectionner un fichier qui contient une liste de vos codes dans le format suivant",
@@ -519,5 +520,12 @@
"algorithm": "Algorithme",
"type": "Type",
"period": "Période",
"digits": "Chiffres"
"digits": "Chiffres",
"importFromGallery": "Importer depuis la galerie",
"errorCouldNotReadImage": "Impossible de lire le fichier sélectionné.",
"errorInvalidQRCode": "QR Code invalide",
"errorInvalidQRCodeBody": "Le code QR scanné n'est pas un compte 2FA valide.",
"errorNoQRCode": "Aucun code QR trouvé",
"errorGenericTitle": "Une erreur s'est produite",
"errorGenericBody": "Une erreur inattendue est survenue lors de l'importation."
}

View File

@@ -111,6 +111,7 @@
"importAegisGuide": "Naudokite „Aegis“ nustatymuose esančią parinktį Eksportuoti slėptuvę.\n\nJei jūsų saugykla užšifruota, turėsite įvesti saugyklos slaptažodį, kad iššifruotumėte saugyklą.",
"import2FasGuide": "Naudokite programoje 2FAS esančią parinktį „Settings->2FAS Backup->Export to file“.\n\nJei atsarginė kopija užšifruota, turėsite įvesti slaptažodį, kad iššifruotumėte atsarginę kopiją.",
"importLastpassGuide": "Naudokite „Lastpass Authenticator“ nustatymuose esančią parinktį „Transfer accounts“ (perkelti paskyras) ir paspauskite „Export accounts to file“ (eksportuoti paskyras į failą). Importuokite atsisiųstą JSON failą.",
"importProtonAuthGuide": "Naudokite „Proton Authenticator“ nustatymuose esančią parinktį „Export“ (eksportuoti), kad eksportuotumėte savo kodus.",
"exportCodes": "Eksportuoti kodus",
"importLabel": "Importuoti",
"importInstruction": "Pasirinkite failą, kuriame yra tokio formato jūsų kodų sąrašas",
@@ -519,5 +520,12 @@
"algorithm": "Algoritmas",
"type": "Tipas",
"period": "Laikotarpis",
"digits": "Skaitmenys"
"digits": "Skaitmenys",
"importFromGallery": "Importuoti iš galerijos",
"errorCouldNotReadImage": "Nepavyko perskaityti pasirinkto vaizdo failo.",
"errorInvalidQRCode": "Netinkamas QR kodas",
"errorInvalidQRCodeBody": "Nuskenuotas QR kodas nėra tinkama 2FA paskyra.",
"errorNoQRCode": "QR kodas nerastas.",
"errorGenericTitle": "Įvyko klaida",
"errorGenericBody": "Importuojant įvyko netikėta klaida."
}

View File

@@ -111,6 +111,7 @@
"importAegisGuide": "Użyj opcji \"Eksportuj sejf\" w ustawieniach Aegis.\n\nJeśli twój sejf jest zaszyfrowany, musisz wprowadzić hasło sejfu, aby odszyfrować sejf.",
"import2FasGuide": "Użyj opcji \"Ustawienia->Kopia Zapasowa-Eksport\" w 2FAS.\n\nJeśli twoja kopia zapasowa jest zaszyfrowana, musisz wprowadzić hasło, aby odszyfrować kopię zapasową",
"importLastpassGuide": "Użyj opcji \"Przenieś konta\" w Ustawieniach Lastpass Authenticator i naciśnij \"Eksportuj konta do pliku\". Zaimportuj pobrany plik JSON.",
"importProtonAuthGuide": "Użyj opcji „Eksportuj” w ustawieniach Proton Authenticator, aby wyeksportować kody.",
"exportCodes": "Eksportuj kody",
"importLabel": "Importuj",
"importInstruction": "Wybierz plik, który zawiera listę twoich kodów w następującym formacie",
@@ -519,5 +520,12 @@
"algorithm": "Algorytm",
"type": "Rodzaj",
"period": "Okres",
"digits": "Cyfry"
"digits": "Cyfry",
"importFromGallery": "Importuj z galerii",
"errorCouldNotReadImage": "Nie można odczytać wybranego pliku obrazu.",
"errorInvalidQRCode": "Nieprawidłowy kod QR",
"errorInvalidQRCodeBody": "Zeskanowany kod QR nie wskazuje na prawidłowe konto 2FA.",
"errorNoQRCode": "Nie znaleziono kodu QR",
"errorGenericTitle": "Wystąpił błąd",
"errorGenericBody": "Podczas importowania wystąpił nieoczekiwany błąd."
}

View File

@@ -18,7 +18,7 @@
"incorrectDetails": "Felaktiga uppgifter",
"pleaseVerifyDetails": "Kontrollera dina detaljer och försök igen",
"codeIssuerHint": "Utfärdare",
"codeSecretKeyHint": "Secret Key",
"codeSecretKeyHint": "Hemlig nyckel",
"secret": "Säkerhetsnyckel",
"all": "Alla",
"notes": "Anteckningar",
@@ -33,7 +33,7 @@
}
}
},
"codeAccountHint": "Konto (du@domän.com)",
"codeAccountHint": "Konto (du@domain.com)",
"codeTagHint": "Tagg",
"accountKeyType": "Typ av nyckel",
"sessionExpired": "Sessionen har gått ut",
@@ -68,7 +68,7 @@
"reportABug": "Rapportera en bugg",
"crashAndErrorReporting": "Krasch och felrapportering",
"reportBug": "Rapportera bugg",
"emailUsMessage": "Skicka e-mail till {email}",
"emailUsMessage": "Skicka e-post till {email}",
"@emailUsMessage": {
"placeholders": {
"email": {
@@ -79,7 +79,7 @@
"contactSupport": "Kontakta support",
"rateUsOnStore": "Betygsätt på {storeName}",
"blog": "Blogg",
"merchandise": "Merchandise",
"merchandise": "Produkter",
"verifyPassword": "Bekräfta lösenord",
"pleaseWait": "Vänligen vänta...",
"generatingEncryptionKeysTitle": "Skapar krypteringsnycklar...",
@@ -104,13 +104,14 @@
"importFromApp": "Importera koder från {appName}",
"importGoogleAuthGuide": "Exportera dina konton från Google Authenticator till en QR-kod med alternativet \"Överföra konton\". Använd sedan en annan enhet och skanna QR-koden.\n\nTips: Du kan använda din bärbara dators webbkamera för att ta en bild av QR-koden.",
"importSelectJsonFile": "Välj JSON-fil",
"importSelectAppExport": "Välj {appName} exportfil",
"importSelectAppExport": "Välj {appName} exporteringsfil",
"importEnteEncGuide": "Välj den krypterade JSON-filen som exporteras från Ente",
"importRaivoGuide": "Använd alternativet \"Exportera OTPs till zip-arkiv\" i Raivos inställningar.\n\nExtrahera zip-filen och importera JSON-filen.",
"importBitwardenGuide": "Använd alternativet \"Exportera valv\" inom Bitwarden Tools och importera den okrypterade JSON-filen.",
"importAegisGuide": "Använd alternativet \"Exportera valvet\" i Aegis inställningar.\n\nOm ditt valv är krypterat måste du ange valvlösenordet för att dekryptera valvet.",
"import2FasGuide": "Använd alternativet \"Inställningar->Säkerhetskopiera -Exportera\" i 2FAS.\n\nOm din säkerhetskopia är krypterad måste du ange lösenordet för att dekryptera säkerhetskopian.",
"importLastpassGuide": "Använd alternativet \"Överför konton\" i LastPass Authenticators inställningar och tryck på \"Exportera konton till fil\". Importera JSON-filen som laddas ner.",
"importProtonAuthGuide": "Använd alternativet \"Exportera\" i Proton Authenticator-inställningarna för att exportera koder.",
"exportCodes": "Exportera koder",
"importLabel": "Importera",
"importInstruction": "Vänligen välj en fil som innehåller en lista över dina koder i följande format",
@@ -119,11 +120,11 @@
"emailVerificationToggle": "E-postverifiering",
"emailVerificationEnableWarning": "För att undvika att bli låst från ditt konto, se till att spara en kopia av din e-post 2FA utanför Ente Auth innan du aktiverar e-postverifiering.",
"authToChangeEmailVerificationSetting": "Autentisera för att ändra din e-postadress",
"authenticateGeneric": "Var god autentisera",
"authenticateGeneric": "Vänligen autentisera",
"authToViewYourRecoveryKey": "Autentisera för att visa din återställningsnyckel",
"authToChangeYourEmail": "Autentisera för att ändra din e-postadress",
"authToChangeYourPassword": "Autentisera för att ändra ditt lösenord",
"authToViewSecrets": "Autentisera för att visa din återställningsnyckel",
"authToViewSecrets": "Vänligen autentisera för att visa din återställningsnyckel",
"authToInitiateSignIn": "Vänligen autentisera för att initiera inloggning för säkerhetskopiering.",
"ok": "OK",
"cancel": "Avbryt",
@@ -147,7 +148,7 @@
"leaveFamily": "Lämna familjen",
"leaveFamilyMessage": "Är du säker på att du vill lämna familjeplanen?",
"inFamilyPlanMessage": "Du är på en familjeplan!",
"hintForMobile": "Håll i på en kod för att redigera eller ta bort.",
"hintForMobile": "Tryck länge på en kod för att redigera eller ta bort.",
"hintForDesktop": "Högerklicka på en kod för att redigera eller ta bort.",
"scan": "Skanna",
"scanACode": "Skanna kod",
@@ -191,7 +192,7 @@
"oopsSomethingWentWrong": "Hoppsan! Något gick fel.",
"selectLanguage": "Välj språk",
"language": "Språk",
"social": "Social",
"social": "Socialt",
"security": "Säkerhet",
"lockscreen": "Låsskärm",
"authToChangeLockscreenSetting": "Vänligen autentisera för att ändra låsskärms inställningar",
@@ -200,7 +201,7 @@
"authToViewYourActiveSessions": "Autentisera för att visa dina aktiva sessioner",
"searchHint": "Sök...",
"search": "Sök",
"sorryUnableToGenCode": "Tyvärr, det gick inte att generera en kod för {issuerName}",
"sorryUnableToGenCode": "Tyvärr, kunde inte generera en kod för {issuerName}",
"noResult": "Inga resultat",
"addCode": "Lägg till kod",
"scanAQrCode": "Skanna en QR-kod",
@@ -215,7 +216,7 @@
"error": "Fel",
"recoveryKeyCopiedToClipboard": "Återställningsnyckel kopierad till urklipp",
"recoveryKeyOnForgotPassword": "Om du glömmer ditt lösenord är det enda sättet du kan återställa dina data med denna nyckel.",
"recoveryKeySaveDescription": "Vi lagrar inte och har därför inte åtkomst till denna nyckel, vänligen spara denna 24 ords nyckel på en säker plats.",
"recoveryKeySaveDescription": "Vi lagrar inte och har därför inte åtkomst till denna nyckel, vänligen spara denna 24 ordsnyckeln på en säker plats.",
"doThisLater": "Gör detta senare",
"saveKey": "Spara nyckel",
"save": "Spara",
@@ -254,7 +255,7 @@
"insecureDevice": "Osäker enhet",
"sorryWeCouldNotGenerateSecureKeysOnThisDevicennplease": "Tyvärr, kunde vi inte generera säkra nycklar på den här enheten.\n\nvänligen registrera dig från en annan enhet.",
"howItWorks": "Så här fungerar det",
"ackPasswordLostWarning": "Jag förstår att om jag förlorar mitt lösenord kan jag förlora mina data eftersom min data är <underline>end-to-end-krypterad</underline>.",
"ackPasswordLostWarning": "Jag förstår att om jag förlorar mitt lösenord kan jag förlora mina data eftersom min data är <underline>totalsträckskrypterad</underline>.",
"loginTerms": "Jag samtycker till <u-terms>användarvillkoren</u-terms> och <u-policy>integritetspolicyn</u-policy>",
"logInLabel": "Logga in",
"logout": "Logga ut",
@@ -278,7 +279,7 @@
"recoveryKeyVerifyReason": "Din återställningsnyckel är det enda sättet att återställa dina foton om du glömmer ditt lösenord. Du hittar din återställningsnyckel i Inställningar > Säkerhet.\n\nAnge din återställningsnyckel här för att verifiera att du har sparat den ordentligt.",
"confirmYourRecoveryKey": "Bekräfta din återställningsnyckel",
"confirm": "Bekräfta",
"emailYourLogs": "Maila dina loggar",
"emailYourLogs": "E-posta dina loggar",
"pleaseSendTheLogsTo": "Vänligen skicka loggarna till \n{toEmail}",
"copyEmailAddress": "Kopiera e-postadress",
"exportLogs": "Exportera loggar",
@@ -297,7 +298,7 @@
"criticalUpdateAvailable": "Kritisk uppdatering tillgänglig",
"updateAvailable": "Uppdatering tillgänglig",
"update": "Uppdatera",
"checking": "Kontrollerar ...",
"checking": "Kontrollerar...",
"youAreOnTheLatestVersion": "Du är på den senaste versionen",
"warning": "Varning",
"exportWarningDesc": "Den exporterade filen innehåller känslig information. Förvara den på ett säkert sätt.",
@@ -306,7 +307,7 @@
"description": "Text for the button to confirm the user understands the warning"
},
"authToExportCodes": "Autentisera för att exportera dina koder",
"importSuccessTitle": "Jippi!",
"importSuccessTitle": "Hurra!",
"importSuccessDesc": "Du har importerat {count} koder!",
"@importSuccessDesc": {
"placeholders": {
@@ -324,7 +325,7 @@
"checkInboxAndSpamFolder": "Vänligen kontrollera din inkorg (och skräppost) för att slutföra verifieringen",
"tapToEnterCode": "Tryck för att ange kod",
"resendEmail": "Skicka e-post igen",
"weHaveSendEmailTo": "Vi har skickat ett mail till <green>{email}</green>",
"weHaveSendEmailTo": "Vi har skickat ett e-postmeddelande till <green>{email}</green>",
"@weHaveSendEmailTo": {
"description": "Text to indicate that we have sent a mail to the user",
"placeholders": {
@@ -362,7 +363,7 @@
"selectExportFormat": "Välj exportformat",
"exportDialogDesc": "Krypterad export skyddas av ett lösenord som du väljer.",
"encrypted": "Krypterad",
"plainText": "Enkel text",
"plainText": "Oformaterad text",
"passwordToEncryptExport": "Lösenord för att kryptera export",
"export": "Exportera",
"useOffline": "Använd utan säkerhetskopior",
@@ -374,14 +375,14 @@
"compactMode": "Kompakt läge",
"shouldHideCode": "Dölj koder",
"doubleTapToViewHiddenCode": "Du kan dubbeltrycka på en post för att visa koden",
"focusOnSearchBar": "Fokusera på sök vid appstart",
"focusOnSearchBar": "Fokusera på sök vid uppstart av app",
"confirmUpdatingkey": "Är du säker på att du vill uppdatera den hemliga nyckeln?",
"minimizeAppOnCopy": "Minimera appen vid kopiering",
"editCodeAuthMessage": "Autentisera för att redigera kod",
"deleteCodeAuthMessage": "Autentisera för att radera kod",
"showQRAuthMessage": "Autentisera för att visa QR-kod",
"confirmAccountDeleteTitle": "Bekräfta radering av kontot",
"confirmAccountDeleteMessage": "Detta konto är kopplat till andra Ente apps, om du använder någon.\n\nDina uppladdade data, över alla Ente appar, kommer att schemaläggas för radering och ditt konto kommer att raderas permanent.",
"confirmAccountDeleteMessage": "Detta konto är kopplat till andra Ente applikationer, om du använder någon.\n\nDina uppladdade data, över alla Ente applikationer, kommer att schemaläggas för radering och ditt konto kommer att raderas permanent.",
"androidBiometricHint": "Verifiera identitet",
"@androidBiometricHint": {
"description": "Hint message advising the user how to authenticate with biometrics. It is used on Android side. Maximum 60 characters."
@@ -390,7 +391,7 @@
"@androidBiometricNotRecognized": {
"description": "Message to let the user know that authentication was failed. It is used on Android side. Maximum 60 characters."
},
"androidBiometricSuccess": "Slutförd",
"androidBiometricSuccess": "Lyckades",
"@androidBiometricSuccess": {
"description": "Message to let the user know that authentication was successful. It is used on Android side. Maximum 60 characters."
},
@@ -441,7 +442,7 @@
"signOutOtherDevices": "Logga ut andra enheter",
"doNotSignOut": "Logga inte ut",
"hearUsWhereTitle": "Hur hörde du talas om Ente? (valfritt)",
"hearUsExplanation": "Vi spårar inte appinstallationer, Det skulle hjälpa oss om du berättade var du hittade oss!",
"hearUsExplanation": "Vi spårar inte installationer. Det skulle hjälpa oss om du berättade hur du hittade oss!",
"recoveryKeySaved": "Återställningsnyckel sparad i nedladdningsmappen!",
"waitingForBrowserRequest": "Väntar på webbläsarbegäran...",
"waitingForVerification": "Väntar på verifiering...",
@@ -488,6 +489,8 @@
"hideContent": "Dölj innehåll",
"hideContentDescriptionAndroid": "Döljer appinnehåll i app-växlaren och inaktiverar skärmdumpar",
"hideContentDescriptioniOS": "Döljer appinnehåll i app-växlaren",
"autoLockFeatureDescription": "Tid efter vilken appen låses efter att ha satts i bakgrunden",
"appLockDescription": "Välj mellan enhetens förvalda låsskärm och en anpassad låsskärm med en PIN-kod eller lösenord.",
"pinLock": "Pinkodslås",
"enterPin": "Ange PIN-kod",
"setNewPin": "Ställ in ny PIN-kod",
@@ -498,9 +501,31 @@
"appLockOfflineModeWarning": "Du har valt att fortsätta utan säkerhetskopior. Om du glömmer ditt applås, kommer du att bli utelåst från att komma åt dina data.",
"duplicateCodes": "Dubblettkoder",
"noDuplicates": "✨ Inga dubbletter",
"youveNoDuplicateCodesThatCanBeCleared": "Du har inga dubbla koder som kan rensas",
"deduplicateCodes": "Deduplicera koder",
"deselectAll": "Avmarkera alla",
"selectAll": "Markera alla",
"deleteDuplicates": "Radera dubbletter",
"plainHTML": "Ren HTML"
"plainHTML": "Ren HTML",
"tellUsWhatYouThink": "Berätta vad du tycker",
"dropReviewiOS": "Skriv en recension på App Store",
"dropReviewAndroid": "Skriv en recension på Play Store",
"supportEnte": "Stöd <bold-green>ente</bold-green>",
"giveUsAStarOnGithub": "Ge oss en stjärna på Github",
"free5GB": "5 GB gratis på <bold-green>ente</bold-green> Foton",
"loginWithAuthAccount": "Logga in med ditt Auth-konto",
"freeStorageOffer": "10% rabatt på <bold-green>ente</bold-green> foton",
"freeStorageOfferDescription": "Använd koden \"AUTH\" för att få 10% rabatt första året",
"advanced": "Avancerad",
"algorithm": "Algoritm",
"type": "Typ",
"period": "Tidsperiod",
"digits": "Siffror",
"importFromGallery": "Importera från galleri",
"errorCouldNotReadImage": "Kunde inte läsa den valda bildfilen.",
"errorInvalidQRCode": "Ogiltig QR-kod",
"errorInvalidQRCodeBody": "Den skannade QR-koden är inte ett giltigt 2FA konto.",
"errorNoQRCode": "Ingen QR-kod hittades",
"errorGenericTitle": "Ett fel inträffade",
"errorGenericBody": "Ett oväntat fel inträffade vid import."
}

View File

@@ -6,7 +6,7 @@
"@counterAppBarTitle": {
"description": "Text shown in the AppBar of the Counter Page"
},
"onBoardingBody": "妥善保管您的两步验证码",
"onBoardingBody": "妥善保管您的双重认证代码",
"onBoardingGetStarted": "开始",
"setupFirstAccount": "设置您的第一个账户",
"importScanQrCode": "扫描二维码",
@@ -111,13 +111,14 @@
"importAegisGuide": "使用 Aegis 设置中的“导出密码库”选项。\n\n如果您的密码库已加密则需要输入密码库密码才能解密密码库。",
"import2FasGuide": "使用 2FAS 中的“设置 -> 备份 -> 导出”选项。\n\n如果您的备份已加密则需要输入密码来解密备份",
"importLastpassGuide": "使用 Lastpass Authenticator 设置中的“转移账户”选项,然后按“将账户导出到文件”。导入下载的 JSON。",
"importProtonAuthGuide": "使用 Proton Authenticator 设置中的“导出”选项导出您的代码。",
"exportCodes": "导出代码",
"importLabel": "导入",
"importInstruction": "请选择一个包含以下格式的代码列表的文件",
"importCodeDelimiterInfo": "代码可以用逗号或换行符分隔",
"selectFile": "选择文件",
"emailVerificationToggle": "电子邮件验证",
"emailVerificationEnableWarning": "为避免被锁在您的账户之外,请在启用电子邮件验证之前确保在 Ente Auth 之外存储电子邮件两步验证的副本。",
"emailVerificationEnableWarning": "为避免被锁在您的账户之外,请在启用电子邮件验证之前确保在 Ente Auth 之外存储电子邮件双重认证的副本。",
"authToChangeEmailVerificationSetting": "请进行身份验证以更改电子邮件验证",
"authenticateGeneric": "请验证",
"authToViewYourRecoveryKey": "请验证以查看您的恢复密钥",
@@ -155,7 +156,7 @@
"verifyEmail": "验证电子邮件",
"enterCodeHint": "从你的身份验证器应用中\n输入6位数字代码",
"lostDeviceTitle": "丢失了设备吗?",
"twoFactorAuthTitle": "两步验证",
"twoFactorAuthTitle": "双重认证",
"passkeyAuthTitle": "通行密钥验证",
"verifyPasskey": "验证通行密钥",
"loginWithTOTP": "使用 TOTP 登录",
@@ -519,5 +520,12 @@
"algorithm": "算法",
"type": "类型",
"period": "周期",
"digits": "数字"
"digits": "数字",
"importFromGallery": "从图库导入",
"errorCouldNotReadImage": "无法读取所选图片文件。",
"errorInvalidQRCode": "二维码无效",
"errorInvalidQRCodeBody": "扫描的二维码不是有效的双重认证账户。",
"errorNoQRCode": "未找到二维码",
"errorGenericTitle": "出错了",
"errorGenericBody": "导入时发生意外错误。"
}

View File

@@ -382,7 +382,8 @@ class _HomePageState extends State<HomePage> {
final bool shouldShowLockScreen =
await LockScreenSettings.instance.shouldShowLockScreen();
if (shouldShowLockScreen) {
await AppLock.of(context)!.showLockScreen();
// Manual lock: do not auto-prompt Touch ID; wait for user tap
await AppLock.of(context)!.showManualLockScreen();
} else {
await showDialogWidget(
context: context,

View File

@@ -7,8 +7,7 @@ import 'package:ente_events/event_bus.dart';
import 'package:ente_events/models/signed_in_event.dart';
import 'package:ente_events/models/signed_out_event.dart';
import 'package:ente_strings/l10n/strings_localizations.dart';
import 'package:ente_ui/theme/colors.dart';
import 'package:ente_ui/theme/ente_theme_data.dart';
import "package:ente_ui/theme/ente_theme_data.dart";
import 'package:ente_ui/utils/window_listener_service.dart';
import 'package:flutter/foundation.dart';
import "package:flutter/material.dart";
@@ -87,37 +86,14 @@ class _AppState extends State<App>
@override
Widget build(BuildContext context) {
final schemes = ColorSchemeBuilder.fromCustomColors(
primary700: const Color(0xFF1565C0), // Dark blue
primary500: const Color(0xFF2196F3), // Material blue
primary400: const Color(0xFF42A5F5), // Light blue
primary300: const Color(0xFF90CAF9), // Very light blue
iconButtonColor: const Color(0xFF1976D2), // Custom icon color
gradientButtonBgColors: const [
Color(0xFF1565C0),
Color(0xFF2196F3),
Color(0xFF42A5F5),
],
);
final lightTheme = createAppThemeData(
brightness: Brightness.light,
colorScheme: schemes.light,
);
final darkTheme = createAppThemeData(
brightness: Brightness.dark,
colorScheme: schemes.dark,
);
Widget buildApp() {
if (Platform.isAndroid ||
Platform.isWindows ||
Platform.isLinux ||
kDebugMode) {
return AdaptiveTheme(
light: lightTheme,
dark: darkTheme,
light: lightThemeData,
dark: darkThemeData,
initial: AdaptiveThemeMode.system,
builder: (lightTheme, dartTheme) => MaterialApp(
title: "ente",
@@ -142,8 +118,8 @@ class _AppState extends State<App>
return MaterialApp(
title: "ente",
themeMode: ThemeMode.system,
theme: lightTheme,
darkTheme: darkTheme,
theme: lightThemeData,
darkTheme: darkThemeData,
debugShowCheckedModeBanner: false,
locale: locale,
supportedLocales: appSupportedLocales,

View File

@@ -10,6 +10,7 @@ import 'package:ente_lock_screen/ui/lock_screen.dart';
import 'package:ente_logging/logging.dart';
import 'package:ente_network/network.dart';
import "package:ente_strings/l10n/strings_localizations.dart";
import "package:ente_ui/theme/ente_theme_data.dart";
import "package:ente_ui/theme/theme_config.dart";
import 'package:ente_ui/utils/window_listener_service.dart';
import 'package:ente_utils/platform_util.dart';
@@ -103,6 +104,8 @@ Future<void> _runInForeground() async {
lockScreen: LockScreen(Configuration.instance),
enabled: await LockScreenSettings.instance.shouldShowLockScreen(),
locale: locale,
lightTheme: lightThemeData,
darkTheme: darkThemeData,
savedThemeMode: savedThemeMode,
supportedLocales: appSupportedLocales,
localizationsDelegates: const [

View File

@@ -52,3 +52,4 @@ lib/generated/intl/app_localizations*.dart
# Generated rust bindings
lib/src/rust/*
rust/src/frb_generated*
test/**/*.mocks.dart

View File

@@ -0,0 +1,67 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Development Commands
### Prerequisites
- Flutter v3.32.8
- Rust (for Flutter Rust Bridge)
- Flutter Rust Bridge: `cargo install flutter_rust_bridge_codegen`
### Development
- **Run development build**: `flutter run -t lib/main.dart --flavor independent`
- **Alternative with env file**: `./run.sh` (uses .env file for configuration)
- **Generate Rust bindings**: `flutter_rust_bridge_codegen generate`
### Build Commands
- **Build APK**: `flutter build apk --release --flavor independent`
- **Build iOS**: `flutter build ios`
- **iOS setup**: `cd ios && pod install && cd ..`
### Testing
- **Run all tests**: `flutter test`
- **Run specific test**: `flutter test test/path/to/test_file.dart`
- **Integration tests**: `flutter test integration_test/`
- **Performance tests**: Use scripts in `scripts/` directory
### Code Generation
- **Generate localization**: Automatically runs with flutter (see l10n.yaml)
- **Generate launcher icons**: `dart run flutter_launcher_icons`
- **Generate splash screen**: `dart run flutter_native_splash:create`
## Architecture
### Core Services Structure
The app follows a service-oriented architecture with dependency injection via `service_locator.dart`:
- **Authentication**: `services/account/` - handles user authentication, billing, passkeys
- **Sync Services**: `services/sync/` - local and remote file synchronization
- **Machine Learning**: `services/machine_learning/` - face detection, semantic search, ML models
- **Collections**: `services/collections_service.dart` - manages photo albums and folders
- **File Management**: `services/files_service.dart`, `utils/file_uploader.dart`
### Data Layer
- **Databases**: SQLite with migrations via `sqflite_migration`
- `db/files_db.dart` - main file storage
- `db/collections_db.dart` - collections and albums
- `db/ml/` - ML-related data (embeddings, face data)
- **Models**: `models/` directory contains data models with freezed for immutables
### UI Architecture
- **State Management**: Event-based architecture using `event_bus`
- **Navigation**: Standard Flutter navigation with named routes
- **Theming**: Custom theme system in `theme/` and `ente_theme_data.dart`
- **Main screens**: Located in `ui/` with feature-specific subdirectories
### Key Features Implementation
- **End-to-end encryption**: Uses `ente_crypto` plugin with libsodium
- **Photo upload**: Background upload via `workmanager` and custom uploader
- **Video playback**: Multiple players (media_kit, video_player, chewie)
- **Image editing**: `pro_image_editor` and custom video editor
- **Home widgets**: iOS and Android widgets in `ios/EnteWidget/` and via `home_widget` package
### Platform-Specific Code
- **Android**: Flavors configured in `android/app/build.gradle`
- **iOS**: Widget extensions in `ios/EnteWidget/`
- **Rust integration**: FFI bridge in `rust/` directory

View File

@@ -75,3 +75,4 @@ analyzer:
- rust_builder/**
- lib/src/rust/**
- rust/src/**
- test/**

View File

@@ -135,6 +135,17 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<activity-alias
android:name="${applicationId}.IconDuckyHuggingE"
android:icon="@mipmap/icon_ducky_hugging_e"
android:enabled="false"
android:exported="true"
android:targetActivity=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity-alias>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_ducky_hugging_e_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

View File

@@ -0,0 +1,52 @@
<svg width="198" height="150" viewBox="0 0 198 150" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.2" d="M103.208 1.51891C70.0589 1.51891 17.0832 -6.28475 9.02179 30.1961C0.957739 66.6797 0.325369 122.498 30.8944 124.356C61.4634 126.213 136.678 126.856 160.318 124.356C183.957 121.855 185.312 83.2462 185.312 57.3881C185.312 31.53 174.179 -0.973365 123.125 0.817466C107.034 1.38341 103.21 1.51891 103.21 1.51891H103.208Z" fill="white"/>
<path d="M141.928 142.014C168.856 142.014 190.684 139.1 190.684 135.504C190.684 131.909 168.856 128.995 141.928 128.995C115.001 128.995 93.1719 131.909 93.1719 135.504C93.1719 139.1 115.001 142.014 141.928 142.014Z" fill="#343434"/>
<path d="M67.2538 149.392C104.397 149.392 134.508 145.779 134.508 141.321C134.508 136.864 104.397 133.251 67.2538 133.251C30.1105 133.251 0 136.864 0 141.321C0 145.779 30.1105 149.392 67.2538 149.392Z" fill="#343434"/>
<path d="M111.039 120.086C101.394 128.801 101.925 135.497 113.988 136.559C126.051 137.622 134.713 135.151 131.923 127.605C129.159 120.086 111.039 120.086 111.039 120.086Z" fill="#FF814A"/>
<path d="M118.989 137.665C117.408 137.665 115.71 137.585 113.909 137.428C108.141 136.921 104.71 135.165 103.722 132.21C102.595 128.852 104.923 124.436 110.452 119.438C110.612 119.294 110.822 119.212 111.037 119.212C111.802 119.212 129.801 119.305 132.739 127.303C133.675 129.829 133.483 131.982 132.173 133.698C130.186 136.299 125.584 137.662 118.989 137.662V137.665ZM111.369 120.963C106.642 125.299 104.514 129.088 105.375 131.657C106.113 133.86 109.118 135.258 114.063 135.691C122.635 136.445 128.733 135.335 130.789 132.643C131.727 131.416 131.828 129.869 131.105 127.908C128.778 121.574 113.803 121.011 111.372 120.963H111.369Z" fill="#1C1C1C"/>
<path d="M157.878 120.086C167.523 128.801 166.992 135.497 154.929 136.559C142.866 137.622 134.204 135.151 136.994 127.605C139.757 120.086 157.878 120.086 157.878 120.086Z" fill="#FF814A"/>
<path d="M149.927 137.665C143.332 137.665 138.73 136.302 136.743 133.701C135.433 131.984 135.241 129.832 136.177 127.303C139.115 119.305 157.114 119.215 157.879 119.215C158.094 119.215 158.304 119.295 158.464 119.441C163.993 124.436 166.321 128.852 165.194 132.213C164.203 135.168 160.775 136.921 155.007 137.431C153.205 137.591 151.508 137.668 149.927 137.668V137.665ZM157.547 120.963C155.116 121.011 140.141 121.574 137.813 127.906C137.088 129.867 137.192 131.416 138.13 132.641C140.186 135.332 146.281 136.446 154.855 135.688C159.8 135.253 162.803 133.858 163.544 131.655C164.405 129.088 162.277 125.297 157.55 120.961L157.547 120.963Z" fill="#1C1C1C"/>
<path d="M100.616 46.8054C80.2899 35.6035 65.3176 38.7095 71.7396 52.8316L100.616 46.8054Z" fill="#F4D93B"/>
<path d="M71.743 53.7029C71.4108 53.7029 71.0946 53.5142 70.9485 53.1927C68.0231 46.7628 69.6599 43.3299 71.5463 41.5842C76.3981 37.0912 87.9747 38.8422 101.039 46.0427C101.462 46.2739 101.616 46.8053 101.382 47.2277C101.148 47.6502 100.619 47.8016 100.197 47.5705C86.1015 39.8014 76.2466 39.6101 72.7314 42.8649C70.077 45.3227 71.1611 49.4463 72.5374 52.4727C72.7367 52.9111 72.5427 53.4292 72.1043 53.6285C71.9874 53.6816 71.8652 53.7082 71.743 53.7082V53.7029Z" fill="#232323"/>
<path d="M174.283 115.362C168.265 123.375 156.484 129.659 133.976 129.659C131.848 129.659 129.815 129.604 127.876 129.495C123.46 129.247 119.517 128.729 116.001 127.985C101.956 125.015 94.7002 118.444 91.084 111.161C86.2907 101.506 87.8929 90.6011 88.5518 85.1861C89.6146 76.4552 90.7253 70.2882 91.7456 64.4747C92.0059 62.9814 92.261 61.5147 92.5081 60.0321C93.1299 56.3043 93.903 52.8635 94.8117 49.6883C101.154 27.4916 114.091 18.3488 127.926 16.3932C129.93 16.1089 131.952 15.9761 133.976 15.9761C138.254 15.9761 142.59 16.5022 146.775 17.7696C160.206 21.8375 172.086 33.5469 175.444 60.0321C176.372 67.3389 178.069 74.2153 179.403 85.1861C180.161 91.4062 182.161 104.867 174.283 115.362Z" fill="#F4D93B"/>
<path d="M129.171 17.7694C129.171 17.7694 107.25 19.5257 97.8895 49.7174L95.2936 51.1947L94.8047 49.6882C101.147 27.4914 114.084 18.3486 127.919 16.3931L129.171 17.7694Z" fill="white"/>
<path d="M174.284 115.362C175.424 110.515 177.432 104.298 176.872 97.9079C174.932 75.8546 175.277 30.1539 144.047 18.8191L146.776 17.7695C160.207 21.8374 172.086 33.5469 175.445 60.0321C176.372 67.3389 178.07 74.2152 179.404 85.1861C180.161 91.4062 182.162 104.867 174.284 115.362Z" fill="white"/>
<path d="M121.074 101.628C120.253 107.898 118.297 117.926 116.001 127.986C101.956 125.015 94.7002 118.444 91.084 111.161C86.2907 101.506 87.8929 90.6013 88.5518 85.1863C89.6146 76.4553 90.7253 70.2884 91.7456 64.4748L94.368 54.9733C137.813 54.333 124.794 73.1978 121.074 101.628Z" fill="#E2B639"/>
<path d="M133.974 130.53C115.279 130.53 101.954 126.178 94.3657 117.596C84.9758 106.973 86.7453 92.6493 87.5956 85.7676L87.6806 85.0795C88.685 76.8215 89.7132 70.9734 90.707 65.3166C91.0338 63.454 91.342 61.6951 91.6449 59.8883C94.2674 44.1561 99.7674 32.343 107.996 24.7784C114.979 18.3564 123.72 15.1016 133.974 15.1016C144.227 15.1016 153.445 18.205 160.382 24.3214C168.906 31.8381 174.265 43.816 176.306 59.9228C176.707 63.0953 177.267 66.2518 177.863 69.5944C178.636 73.9492 179.513 78.8833 180.264 85.0795L180.349 85.7676C181.2 92.652 182.972 106.973 173.579 117.596C165.994 126.178 152.666 130.53 133.971 130.53H133.974ZM133.974 16.8472C124.172 16.8472 115.829 19.948 109.176 26.0644C101.242 33.3606 95.9227 44.8363 93.364 60.1779C93.0611 61.9926 92.7529 63.7542 92.4234 65.6195C91.4323 71.2523 90.4094 77.0792 89.4103 85.292L89.3253 85.9855C88.5069 92.6148 86.8011 106.41 95.6703 116.446C102.911 124.637 115.797 128.79 133.971 128.79C152.145 128.79 165.032 124.637 172.272 116.446C181.141 106.41 179.438 92.6148 178.617 85.9855L178.532 85.2947C177.785 79.1437 176.914 74.2335 176.143 69.9026C175.546 66.5388 174.982 63.361 174.573 60.146C170.035 24.3613 149.988 16.8499 133.971 16.8499L133.974 16.8472Z" fill="#1C1C1C"/>
<path d="M135.676 20.3044L126.961 19.5631C127.338 17.4136 127.864 15.5564 128.483 13.994C131.411 6.59157 136.401 5.82901 137.307 12.1208C137.307 12.1208 140.036 6.16113 147.21 7.88022C149.187 8.35583 150.143 9.60198 150.228 11.1324C150.454 15.1312 144.726 21.0776 135.676 20.3044Z" fill="#F4D93B"/>
<path d="M150.229 11.1324C147.761 9.91816 140.659 9.25922 138.754 11.2706C136.508 13.6406 137.282 12.0863 136.928 11.244C136.575 10.3991 130.108 11.0235 127.969 18.3914L128.484 13.994C131.412 6.59157 136.402 5.82901 137.308 12.1208C137.308 12.1208 140.037 6.16113 147.211 7.88022C149.188 8.35583 150.144 9.60198 150.229 11.1324Z" fill="white"/>
<path d="M126.453 19.475C126.299 15.4656 128.345 9.79818 131.853 7.54768C133.524 6.48222 135.604 6.64961 136.824 8.29697C137.621 9.36243 137.95 10.768 138.043 12.0115L136.619 11.8069C136.725 11.581 136.816 11.4216 136.922 11.2409C138.968 7.76025 142.871 6.13681 146.796 6.95251C152.516 7.97281 151.823 13.4516 148.488 16.7782C146.277 19.0287 143.31 20.4236 140.225 20.8966C138.692 21.1251 137.129 21.1011 135.623 20.8514C135.32 20.8009 135.115 20.5166 135.166 20.2137C135.211 19.9427 135.445 19.7514 135.71 19.7487C137.156 19.7434 138.58 19.6371 139.967 19.3714C142.704 18.8586 145.371 17.6523 147.308 15.641C148.873 14.0547 150.592 10.8185 148.26 9.20301C147.771 8.86557 147.075 8.67958 146.487 8.56533C145.812 8.43779 145.13 8.37136 144.457 8.38996C141.747 8.40591 139.242 10.0665 137.982 12.4286C137.663 13.0769 136.678 12.9255 136.558 12.224C136.21 10.6936 135.341 8.16146 133.375 9.0861C129.985 11.0231 128.956 16.2149 127.45 19.6478C127.253 20.1606 126.461 20.025 126.451 19.4724L126.453 19.475Z" fill="#1C1C1C"/>
<path d="M121.04 44.7142C127.356 40.8907 130.499 40.6516 136.932 44.8629C138.415 45.8407 137.939 47.3233 136.634 47.8574C133.52 49.1036 124.715 49.1912 121.216 48.0062C119.614 47.4429 119.319 45.7823 121.038 44.7142H121.04Z" fill="#FF814A"/>
<path d="M120.694 44.1455C122.944 42.8516 125.41 41.5815 128.054 41.2759C130.761 40.9624 133.434 42.0545 135.708 43.3405C136.096 43.545 136.466 43.7762 136.838 43.9994C137.263 44.2545 137.698 44.4989 138.031 44.9054C139.064 46.048 138.522 47.7644 137.22 48.4313C136.078 48.984 134.784 49.2045 133.546 49.3905C129.626 49.8581 125.622 49.9166 121.764 48.976C121.345 48.8352 120.744 48.6917 120.367 48.4286C118.541 47.2941 118.956 45.11 120.694 44.1455ZM121.39 45.2827C120.51 45.8274 120.205 46.8052 121.334 47.233C123.162 47.7963 125.115 47.8574 127.068 47.9504C129.967 48.0035 132.908 47.9902 135.738 47.3286C135.868 47.2808 136.378 47.1453 136.495 47.0736C137.446 46.5714 137.202 45.7982 136.365 45.3173C135.929 45.033 135.491 44.7248 135.044 44.4644C132.937 43.1731 130.636 42.1288 128.16 42.3706C125.67 42.6576 123.502 43.9675 121.387 45.2827H121.39Z" fill="#232323"/>
<path d="M111.373 54.4653L109.935 65.7577L100.806 137.407C100.806 142.152 83.2321 146 61.5535 146C60.5518 146 59.5607 145.992 58.5776 145.976C44.0969 145.737 31.8108 143.781 25.9601 141.036C23.6112 139.934 22.3013 138.703 22.3013 137.407L11.7344 54.4653H111.373Z" fill="#9F9DA8"/>
<path d="M109.936 65.7583L100.806 137.408C100.806 142.153 83.2326 146 61.554 146C60.5523 146 59.5612 145.992 58.5781 145.976C58.703 88.4253 69.0175 62.1793 109.936 65.7583Z" fill="#8A8899"/>
<path d="M25.9601 141.036C23.6112 139.934 22.3013 138.703 22.3013 137.407L11.7344 54.4653H12.5766L15.5126 57.4545L25.9601 141.036Z" fill="white"/>
<path d="M61.5596 146.871C51.0139 146.871 41.0899 145.97 33.6183 144.334C25.5915 142.577 21.4944 140.266 21.4359 137.468L10.8743 54.5742C10.8424 54.3244 10.9194 54.0747 11.0842 53.8887C11.2489 53.7027 11.488 53.5938 11.7378 53.5938H111.376C111.626 53.5938 111.865 53.7027 112.03 53.8887C112.194 54.0747 112.271 54.3271 112.24 54.5742L101.678 137.468C101.619 140.268 97.5223 142.577 89.4955 144.334C82.0213 145.97 72.1 146.871 61.5542 146.871H61.5596ZM12.7315 55.3368L23.1736 137.298C23.1789 137.335 23.1816 137.372 23.1816 137.407C23.1816 137.885 23.7023 140.377 33.993 142.63C41.3476 144.241 51.1387 145.125 61.5622 145.125C71.9857 145.125 81.7768 144.238 89.1314 142.63C99.4221 140.377 99.9429 137.885 99.9429 137.407C99.9429 137.37 99.9429 137.332 99.9508 137.298L110.393 55.3368H12.7342H12.7315Z" fill="#1C1C1C"/>
<path d="M111.373 54.4654C111.373 57.6273 101.868 60.3986 87.6135 61.9396C80.0331 62.7606 71.1081 63.2336 61.5535 63.2336C47.0169 63.2336 33.9364 62.1389 24.8282 60.3906C16.6977 58.8283 11.7344 56.7505 11.7344 54.4654C11.7344 51.2451 21.5919 48.4313 36.2826 46.9062C43.6903 46.1383 52.3283 45.6973 61.5535 45.6973C69.3014 45.6973 76.6374 46.0081 83.1763 46.5634C99.8597 47.9796 111.373 50.9847 111.373 54.4654Z" fill="#232323"/>
<path d="M61.5578 64.1055C47.5792 64.1055 34.1321 63.064 24.6677 61.2466C15.509 59.4876 10.8672 57.2052 10.8672 54.4658C10.8672 52.4306 13.2824 50.7141 18.2511 49.2156C22.5979 47.9057 28.8021 46.8057 36.1966 46.0404C43.8567 45.246 52.6249 44.8262 61.5578 44.8262C69.1622 44.8262 76.461 45.1184 83.255 45.695C91.6273 46.4071 98.7215 47.5151 103.767 48.8994C109.475 50.467 112.251 52.2871 112.251 54.4658C112.251 56.4692 109.913 58.1618 105.101 59.6444C100.903 60.9383 94.8901 62.0304 87.7135 62.8062C79.8673 63.6565 70.8228 64.1055 61.5605 64.1055H61.5578ZM61.5578 46.5692C52.686 46.5692 43.979 46.9863 36.3772 47.7728C17.5788 49.7257 12.6102 53.0045 12.6102 54.4658C12.6102 55.7067 15.8544 57.7791 24.9972 59.5354C34.3579 61.3316 47.6829 62.3625 61.5578 62.3625C70.7591 62.3625 79.7371 61.9161 87.5249 61.0738C105.704 59.1077 110.505 55.8927 110.505 54.4658C110.505 52.574 103.209 49.1385 83.1062 47.4327C76.3627 46.8588 69.1117 46.5692 61.5578 46.5692Z" fill="#232323"/>
<path d="M65.278 73.9174C50.4545 73.9174 32.1025 71.7679 12.7567 64.3973C12.483 64.2937 12.3449 63.9855 12.4485 63.7118C12.5521 63.4381 12.8603 63.3 13.134 63.4036C39.8503 73.5826 64.6696 73.7288 80.7844 72.0575C98.257 70.2454 109.555 65.9676 109.666 65.9251C109.94 65.8188 110.248 65.957 110.352 66.2306C110.458 66.5043 110.32 66.8125 110.046 66.9162C109.932 66.9587 98.5333 71.279 80.9305 73.1097C76.3578 73.5853 71.0836 73.9147 65.2727 73.9147L65.278 73.9174Z" fill="#1C1C1C"/>
<path d="M59.9036 138.648C48.4173 138.648 34.9515 137.13 21.3981 132.231C21.1217 132.13 20.9782 131.827 21.0792 131.551C21.1802 131.274 21.4831 131.131 21.7594 131.232C42.2317 138.632 62.5234 138.241 75.9413 136.61C90.4965 134.84 100.33 131.269 100.428 131.232C100.705 131.131 101.01 131.272 101.111 131.548C101.212 131.824 101.071 132.13 100.795 132.231C100.697 132.268 90.7702 135.874 76.1008 137.662C71.4351 138.23 65.943 138.65 59.9036 138.65V138.648Z" fill="#1C1C1C"/>
<path d="M28.3625 125.363C27.1482 125.363 26.1093 124.443 25.9844 123.208L21.34 76.6222C21.2098 75.3069 22.1689 74.1352 23.4815 74.005C24.7941 73.8748 25.9658 74.834 26.0987 76.1466L30.7432 122.732C30.8733 124.047 29.9142 125.219 28.6016 125.349C28.5219 125.357 28.4422 125.363 28.3625 125.363Z" fill="#232323"/>
<path d="M43.4128 127.828C42.1215 127.828 41.056 126.797 41.0242 125.498L39.8524 80.3419C39.8179 79.0214 40.8594 77.924 42.1799 77.8895C43.4952 77.855 44.5978 78.8965 44.6324 80.2171L45.8041 125.373C45.8387 126.694 44.7971 127.791 43.4766 127.826C43.4553 127.826 43.4341 127.826 43.4128 127.826V127.828Z" fill="#232323"/>
<path d="M60.6807 128.995C60.6807 128.995 60.6568 128.995 60.6435 128.995C59.323 128.976 58.2681 127.89 58.2894 126.569L58.9643 81.4077C58.9829 80.0872 60.0749 79.0297 61.3901 79.0536C62.7107 79.0722 63.7655 80.1589 63.7442 81.4795L63.0694 126.641C63.0508 127.948 61.9826 128.998 60.678 128.998L60.6807 128.995Z" fill="#232323"/>
<path d="M93.2992 125.362C93.2195 125.362 93.1398 125.36 93.0601 125.349C91.7449 125.219 90.7857 124.047 90.9185 122.732L95.563 76.1465C95.6932 74.8313 96.8676 73.8721 98.1802 74.005C99.4954 74.1352 100.455 75.3069 100.322 76.6221L95.6773 123.208C95.5551 124.441 94.5135 125.362 93.2992 125.362Z" fill="#232323"/>
<path d="M78.2515 127.828C78.2303 127.828 78.209 127.828 78.1877 127.828C76.8672 127.794 75.8257 126.696 75.8602 125.376L77.0319 80.2199C77.0665 78.9206 78.1293 77.8896 79.4206 77.8896C79.4419 77.8896 79.4631 77.8896 79.4844 77.8896C80.8049 77.9242 81.8465 79.0215 81.8119 80.3421L80.6402 125.498C80.6056 126.797 79.5428 127.828 78.2515 127.828Z" fill="#232323"/>
<path d="M92.3543 60.3824L87.6115 61.9394C80.031 62.7605 71.1061 63.2334 61.5514 63.2334C49.0342 63.2334 37.5984 62.423 28.8462 61.0786C27.4353 60.8633 26.0908 60.6322 24.8261 60.3904L24.7969 60.3824C24.7969 60.3824 24.7995 60.3718 24.7995 60.3665C25.7587 55.1507 30.815 50.5116 36.2805 46.906C40.9622 43.8186 45.9414 41.4963 48.8987 40.2316C50.5381 39.5301 51.5584 39.1528 51.5584 39.1528L74.5708 41.313C78.0409 42.7265 80.8733 44.5731 83.1743 46.5633C90.3801 52.7833 92.3543 60.3824 92.3543 60.3824Z" fill="#20D34F"/>
<path d="M50.7588 41.2815C50.7588 41.2815 31.785 53.0202 28.8411 61.0789C28.6816 61.52 28.5674 61.9478 28.5089 62.3623L24.6562 61.2463L24.821 60.3907L24.7944 60.3668C25.7536 55.1511 30.8099 50.5119 36.2754 46.9064C40.9571 43.8189 45.9363 41.4967 48.8936 40.2319L50.7588 41.2815Z" fill="white"/>
<path d="M61.5571 64.1048C47.5785 64.1048 34.1313 63.0633 24.667 61.2459C24.6457 61.2406 24.6245 61.2379 24.6032 61.2326L24.574 61.2246C24.1356 61.1077 23.8619 60.672 23.9443 60.2256C24.7999 55.5306 28.7907 50.8038 35.8052 46.1779C43.0828 41.3794 51.1814 38.3636 51.2611 38.3344C51.3833 38.2892 51.5135 38.2733 51.6437 38.2839L74.6562 40.4441C74.7412 40.4521 74.8236 40.4733 74.9033 40.5052C78.1687 41.8364 81.1446 43.6511 83.7485 45.9043C91.0765 52.228 93.1198 59.843 93.2022 60.1645C93.3191 60.6109 93.0667 61.0679 92.6309 61.2113L87.8881 62.7684C87.8297 62.787 87.7712 62.8002 87.7101 62.8056C79.8639 63.6558 70.8194 64.1048 61.5571 64.1048ZM25.8547 59.6942C35.1622 61.392 48.1019 62.3618 61.5597 62.3618C70.7264 62.3618 79.67 61.9208 87.4338 61.0838L91.2705 59.8244C90.5584 57.7705 88.191 52.0393 82.6113 47.2248C80.1828 45.1231 77.4088 43.4253 74.3665 42.1711L51.6836 40.0429C50.456 40.5132 43.2715 43.3482 36.7671 47.6366C30.6054 51.7019 26.9388 55.7538 25.8547 59.6968V59.6942Z" fill="#232323"/>
<path d="M73.0661 37.8245L52.6549 36.9291C52.2006 36.201 51.8684 35.5527 51.6399 34.9762C49.8704 30.4938 54.2491 30.2865 54.2491 30.2865C55.676 24.7387 60.1185 25.8094 63.469 27.5764C65.7354 28.772 67.5023 30.2865 67.5023 30.2865C79.2251 24.4411 73.0661 37.8245 73.0661 37.8245Z" fill="#20D34F"/>
<path d="M65.7886 29.5904C65.5229 29.5319 56.7547 27.5631 55.5272 31.9764C55.5272 31.9764 53.0535 32.545 53.9834 36.6209L51.6399 34.9762C49.8704 30.4938 54.2491 30.2865 54.2491 30.2865C55.676 24.7387 60.1185 25.8094 63.469 27.5764L65.7886 29.5904Z" fill="white"/>
<path d="M73.0599 38.696C73.0599 38.696 73.0334 38.696 73.0227 38.696L52.6115 37.8006C52.3245 37.7873 52.0615 37.6358 51.91 37.3914C50.353 34.8991 49.9545 32.8877 50.725 31.4158C51.3972 30.1298 52.755 29.6701 53.5601 29.5054C54.227 27.3904 55.4093 26.0539 57.0833 25.5251C60.9253 24.3135 66.0746 28.0413 67.6183 29.2583C70.9768 27.672 73.2459 27.5152 74.5479 28.7747C75.6479 29.8428 75.8418 31.7957 75.1218 34.5829C74.6302 36.4907 73.8836 38.1194 73.8517 38.1885C73.7082 38.4994 73.4 38.696 73.0599 38.696ZM53.1535 36.0788L72.5019 36.9264C73.5249 34.4899 74.3751 31.0358 73.3336 30.0261C72.6082 29.322 70.6234 29.702 67.8867 31.065C67.5732 31.2218 67.1959 31.174 66.9302 30.9481C65.336 29.5851 60.5533 26.2558 57.604 27.1884C56.3845 27.5737 55.5608 28.6578 55.0852 30.5044C54.9895 30.8737 54.6654 31.1394 54.2828 31.158C54.2721 31.158 52.7683 31.2563 52.2634 32.2315C51.8489 33.0312 52.1651 34.389 53.1509 36.0788H53.1535Z" fill="#232323"/>
<path d="M74.6803 37.9618L51.0823 36.4317C49.9841 36.3605 49.036 37.1931 48.9648 38.2913C48.8936 39.3896 49.7262 40.3376 50.8244 40.4089L74.4224 41.939C75.5206 42.0102 76.4687 41.1776 76.5399 40.0794C76.6111 38.9811 75.7785 38.033 74.6803 37.9618Z" fill="#9F9DA8"/>
<path d="M74.5503 42.8173C74.4866 42.8173 74.4254 42.8173 74.3617 42.812L50.7647 41.2816C50.0021 41.2311 49.3033 40.8883 48.7985 40.3144C48.2937 39.7405 48.0439 39.0019 48.0917 38.2393C48.1422 37.4767 48.485 36.7779 49.0589 36.2731C49.6355 35.7683 50.3715 35.5185 51.134 35.5663L74.731 37.0968C75.4936 37.1473 76.1924 37.49 76.6972 38.0639C77.202 38.6378 77.4518 39.3765 77.404 40.1391C77.3535 40.9016 77.0107 41.6004 76.4368 42.1053C75.9107 42.5676 75.2465 42.8173 74.5503 42.8173ZM74.4733 41.0717C74.7735 41.0903 75.0605 40.992 75.2863 40.7953C75.5122 40.5987 75.645 40.3251 75.6663 40.0248C75.6849 39.7246 75.5866 39.4376 75.3899 39.2118C75.1933 38.9859 74.9197 38.8531 74.6194 38.8318L51.0224 37.3014C50.7222 37.2828 50.4352 37.3811 50.2094 37.5777C49.9835 37.7743 49.8507 38.048 49.8294 38.3482C49.8108 38.6485 49.9091 38.9354 50.1057 39.1613C50.3024 39.3871 50.576 39.52 50.8763 39.5412L74.4733 41.0717Z" fill="#232323"/>
<path d="M111.235 40.3833C111.062 40.3833 110.889 40.3541 110.719 40.2877C109.964 40.0034 109.582 39.1584 109.869 38.4038C111.349 34.4874 114.266 33.3449 116.506 33.5813C119.158 33.863 121.299 35.9673 121.714 38.7014C121.836 39.4985 121.286 40.2452 120.489 40.3647C119.692 40.4843 118.945 39.9369 118.826 39.1398C118.61 37.7183 117.532 36.6289 116.201 36.4881C114.705 36.3287 113.36 37.4314 112.606 39.4374C112.385 40.022 111.83 40.3833 111.237 40.3833H111.235Z" fill="#1C1C1C"/>
<path d="M147.634 40.3833C147.044 40.3833 146.489 40.022 146.266 39.4374C145.509 37.434 144.164 36.3314 142.671 36.4881C141.34 36.6289 140.261 37.7183 140.046 39.1398C139.924 39.9369 139.18 40.4869 138.383 40.3647C137.585 40.2425 137.035 39.4985 137.158 38.7014C137.572 35.9673 139.714 33.863 142.365 33.5813C144.603 33.3449 147.523 34.4874 149.003 38.4038C149.287 39.1584 148.907 40.0034 148.152 40.2877C147.982 40.3514 147.807 40.3833 147.637 40.3833H147.634Z" fill="#1C1C1C"/>
<path d="M193.199 114.384C187.58 108.953 173.386 102.09 156.054 96.898C138.723 91.7061 123.094 89.6337 115.415 91.0791L111.839 90.0083L110.51 93.9938C108.879 99.4381 126.625 109.561 150.148 116.61C173.67 123.657 194.06 124.959 195.692 119.515L197.02 115.529L193.197 114.384H193.199Z" fill="#B7B6BF"/>
<path d="M184.999 123.75C183.675 123.75 182.344 123.699 181.072 123.617C172.404 123.064 161.333 120.872 149.897 117.445C138.458 114.017 128.006 109.761 120.462 105.456C114.891 102.278 108.498 97.6658 109.672 93.7414C109.672 93.7334 109.677 93.7255 109.68 93.7148L111.009 89.7293C111.157 89.2829 111.633 89.0332 112.085 89.1687L115.459 90.181C123.571 88.7462 139.55 91.0419 156.3 96.061C173.047 101.077 187.658 107.948 193.645 113.605L197.266 114.689C197.492 114.756 197.681 114.913 197.79 115.122C197.898 115.332 197.917 115.577 197.843 115.8L196.517 119.775C195.563 122.908 190.326 123.744 184.993 123.744L184.999 123.75ZM111.341 94.2569C110.605 96.7784 116.014 100.913 121.328 103.944C128.76 108.185 139.083 112.386 150.399 115.776C161.715 119.166 172.646 121.332 181.186 121.877C187.3 122.267 194.102 121.786 194.859 119.265C194.859 119.257 194.864 119.249 194.867 119.238L195.911 116.106L192.951 115.218C192.818 115.178 192.696 115.106 192.595 115.011C186.893 109.503 172.455 102.722 155.806 97.7323C139.157 92.745 123.369 90.468 115.579 91.9346C115.443 91.9612 115.302 91.9532 115.167 91.9134L112.401 91.0844L111.343 94.2542L111.341 94.2569Z" fill="#232323"/>
<path d="M197.02 115.529C195.981 119.004 187.295 119.73 175.033 117.968C168.085 116.972 159.984 115.173 151.476 112.625C127.956 105.578 110.207 95.4524 111.838 90.0082C113.47 84.5639 133.86 85.8659 157.382 92.9123C164.179 94.9502 170.492 97.2432 175.997 99.6053C189.543 105.422 198.181 111.66 197.02 115.529Z" fill="#9F9DA8"/>
<path d="M197.017 115.529C195.979 119.004 187.293 119.73 175.031 117.968C175.326 117.979 175.636 117.995 175.961 118.011C200.565 119.249 197.589 109.599 173.492 99.4966L175.995 99.6055C189.541 105.422 198.179 111.66 197.017 115.529Z" fill="white"/>
<path d="M186.335 119.764C185.011 119.764 183.68 119.714 182.408 119.631C173.74 119.078 162.668 116.886 151.233 113.459C139.794 110.031 129.342 105.775 121.798 101.47C116.226 98.2926 109.834 93.68 111.008 89.7556C112.182 85.8338 120.063 85.4964 126.461 85.9029C135.128 86.4556 146.2 88.6476 157.636 92.0752C178.688 98.383 199.939 108.833 197.858 115.776C196.917 118.919 191.67 119.761 186.332 119.761L186.335 119.764ZM122.696 87.5264C117.783 87.5264 113.282 88.2358 112.677 90.2578C111.922 92.7793 117.34 96.9216 122.659 99.9559C130.091 104.197 140.413 108.397 151.73 111.788C163.043 115.178 173.977 117.343 182.516 117.888C188.63 118.279 195.432 117.798 196.189 115.276C196.944 112.755 191.526 108.612 186.207 105.578C178.775 101.338 168.453 97.1368 157.137 93.7465C145.82 90.3561 134.887 88.1906 126.35 87.6459C125.141 87.5689 123.905 87.5264 122.696 87.5264Z" fill="#232323"/>
<path d="M164.6 106.976C164.412 106.976 164.22 106.949 164.029 106.894C162.974 106.58 162.374 105.469 162.69 104.415C163.928 100.264 161.157 98.4788 154.99 95.6544C154.06 95.2293 153.098 94.7882 152.203 94.3339C149.086 92.7503 146.803 92.2773 145.228 92.8964C143.897 93.4172 142.791 94.8467 141.941 97.1423C141.558 98.1732 140.413 98.702 139.38 98.3194C138.349 97.9368 137.82 96.7916 138.203 95.758C139.465 92.3491 141.341 90.1358 143.777 89.1846C147.369 87.779 151.339 89.4237 154.009 90.7814C154.833 91.1986 155.715 91.6051 156.65 92.0329C161.808 94.395 168.87 97.6312 166.508 105.554C166.25 106.418 165.458 106.979 164.6 106.979V106.976Z" fill="#232323"/>
<path d="M153.389 70.5537C139.259 91.3847 144.509 114.857 171.688 90.1492L153.389 70.5537Z" fill="#F4D93B"/>
<path d="M152.855 101.628C150.517 101.628 148.944 100.804 147.929 99.922C145.285 97.629 144.331 93.2529 145.243 87.6014C146.144 82.0243 148.779 75.7962 152.667 70.0624C152.938 69.6638 153.48 69.5602 153.878 69.8312C154.277 70.1022 154.38 70.6443 154.109 71.0428C150.36 76.5694 147.823 82.5477 146.962 87.8804C146.162 92.841 146.93 96.7495 149.069 98.6067C150.918 100.212 156.766 102.534 171.098 89.5065C171.454 89.1823 172.007 89.2089 172.328 89.5649C172.653 89.921 172.626 90.4736 172.27 90.7978C162.957 99.263 156.811 101.63 152.852 101.63L152.855 101.628Z" fill="#232323"/>
</svg>

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,430 @@
# Faces Through Time - Feature Design Document (Final MVP)
## Executive Summary
"Faces Through Time" is a delightful slideshow feature that automatically displays a person's face photos chronologically across all the years, creating a visual journey of how they've grown and changed. The feature includes simple sharing capabilities to help spread the joy and potentially grow the product organically.
## Core Requirements
### Eligibility Criteria
- **Minimum time span**: 7 consecutive years of photos
- **Photos per year**: At least 4 faces per year
- **Face quality**: Minimum face score of 0.85
- **Age requirement**: All photos must be from after the person turned 5 years old
- **Total faces**: Minimum 28 faces meeting above criteria
### Display Requirements
- **Faces per year**: Exactly 4 (using quantile selection)
- **Display duration**: 2 seconds per face
- **Format**: Single face at a time, full screen
- **Padding**: Standard 40% padding (use existing face crop logic)
## User Experience Flow
### 1. Progressive Discovery
When user navigates to `PeoplePage`:
```
1. Background eligibility check (instant, non-blocking)
2. If eligible → Check if already viewed
3. If not viewed → Start face selection & thumbnail generation
4. Generate thumbnails (max 4 concurrent)
5. When ready → Show banner at top of page
6. User taps banner → Opens slideshow, mark as viewed
```
### 2. Banner & Menu Logic
**First Time (Not Viewed)**:
- Show eye-catching banner at top of `PeoplePage`
- Text: "How [Name] grew over the years"
- Appears only when all thumbnails are ready
**After First View**:
- No banner shown
- Add menu option in top-right overflow menu
- Menu text: "Show face timeline"
- Clicking menu item opens slideshow directly
### 3. Slideshow Page
**Layout**:
- Full-screen face thumbnail display
- Age display OR relative time below face:
- With DOB (age > 5): "Age 7 years 2 months"
- With DOB (current year): "6 months ago"
- Without DOB: "8 years ago"
- Minimal UI overlay
- Auto-advance every 2 seconds
**Interaction Controls**:
- **Tap center**: Pause/Resume
- **Tap and hold**: Pause (release to resume)
- **Tap left side**: Previous face
- **Tap right side**: Next face
- **Close button**: Top-left corner
- **Share button**: Top-right corner
## Face Selection Algorithm
### Simple Quantile Selection
For each eligible year:
1. Get all faces with score ≥ 0.85
2. Filter out faces where person age ≤ 4 years (if DOB available)
3. Sort faces by timestamp
4. Select faces at positions:
- 1st percentile (earliest)
- 25th percentile
- 50th percentile (median)
- 75th percentile
This ensures even distribution across the year without complex logic.
## Sharing Feature (MVP)
### Share Flow
1. User taps share button in slideshow
2. Generate temporary video file:
- 1 second per face (faster than slideshow)
- Include age/year text overlay
- Add subtle Ente watermark
- Resolution: 720p (balance quality/size)
3. Open system share sheet
4. Clean up temp file after sharing
### Video Generation
```dart
// Pseudocode for video generation
final frames = timeline.entries.map((entry) => {
'image': faceThumbnail,
'text': entry.ageText ?? entry.relativeTimeText,
'duration': 1000, // 1 second
});
final videoPath = await generateVideo(frames, watermark: true);
Share.shareFiles([videoPath]);
```
### Privacy Considerations
- Strip all metadata from video
- Don't include person's name in video
- Watermark: "Created with Ente Photos"
- Temporary file deleted after share
## Technical Implementation
### Caching Strategy
**Cache Structure** (JSON file):
```json
{
"personId": "person_123",
"generatedAt": "2024-01-15T10:30:00Z",
"faceIds": ["face_1", "face_2", ..., "face_28"],
"hasBeenViewed": true,
"version": 1
}
```
**Cache Implementation** (Similar to `similar_images_service.dart`):
```dart
Future<String> _getCachePath(String personId) async {
final dir = await getApplicationSupportDirectory();
return "${dir.path}/cache/faces_timeline_${personId}.json";
}
Future<void> _cacheTimeline(FaceTimeline timeline) async {
final cachePath = await _getCachePath(timeline.personId);
await writeToJsonFile(cachePath, timeline.toJson());
}
```
**Cache Rules**:
- Cache persists for 1 year
- Only invalidate if older than 1 year
- One cache file per person
- No limit on number of cached persons
### Thumbnail Generation
**Batch Processing**:
```dart
// Generate thumbnails in batches of 4
for (int i = 0; i < faceIds.length; i += 4) {
final batch = faceIds.skip(i).take(4).toList();
final thumbnails = await Future.wait(
batch.map((faceId) => generateFaceThumbnail(faceId))
);
// Store thumbnails
}
```
**Use Existing Methods**:
```dart
// Use standard face cropping from face_thumbnail_cache.dart
final cropMap = await getCachedFaceCrops(
file,
faces,
useFullFile: true, // Always use full file for quality
useTempCache: false, // Use persistent cache
);
```
### View State Tracking
**Storage**:
```dart
// Simple key-value storage for viewed state
final viewedKey = "faces_timeline_viewed_${personId}";
final hasViewed = prefs.getBool(viewedKey) ?? false;
if (!hasViewed) {
// Show banner
}
// After viewing:
await prefs.setBool(viewedKey, true);
```
## Age Filtering Logic
### When DOB is Available
```dart
bool isEligibleFace(Face face, DateTime? dob, DateTime photoTime) {
if (dob == null) return true;
final ageAtPhoto = photoTime.difference(dob);
final yearsOld = ageAtPhoto.inDays / 365.25;
// Exclude photos where person was 4 or younger
return yearsOld > 4.0;
}
```
### Eligibility Check Update
Must have 7 consecutive years where ALL photos are:
- After person turned 5 (if DOB known)
- Meeting quality threshold (score ≥ 0.85)
## Data Flow Summary
```
PeoplePage Load
Check Eligibility (with age filter)
Check if Viewed
├─→ Not Viewed: Show banner when ready
└─→ Viewed: Add menu option
User Interaction
Load/Generate Timeline
Show Slideshow
Optional: Share as Video
```
## Implementation Components
### New Files Required
1. **Service**: `faces_through_time_service.dart`
- Eligibility checking
- Face selection logic
- Cache management
2. **UI**: `faces_through_time_page.dart`
- Slideshow display
- Auto-advance logic
- Age/time display
3. **Widget**: `faces_timeline_banner.dart`
- Banner component for PeoplePage
- Loading state management
### Database Queries Needed
```dart
// Get person's photo time span
Future<int> getPersonPhotoYearSpan(String personId);
// Get high-quality faces with timestamps
Future<List<FaceWithTimestamp>> getPersonHighQualityFaces(
String personId,
double minScore,
);
```
### Integration Points
1. **PeoplePage** (`people_page.dart`):
- Add `FacesThroughTimeService` initialization
- Add banner widget in header section
- Trigger background processing on page load
2. **Face Quality Check**:
- Use existing `face.score` field
- Filter with score >= 0.85
3. **Thumbnail Generation**:
- Use existing `getCachedFaceCrops` with `useFullFile: true`
- Leverage existing cache system
## Performance Optimizations
### Concurrent Limits
- Max 4 thumbnail generations at once
- Sequential batch processing
- Total generation time: ~7-10 seconds for 28 faces
### Memory Management
- Load 5 thumbnails ahead (current + 4)
- Release thumbnails >5 positions behind
- Peak memory: ~15MB (5 thumbnails × 3MB)
### Background Processing
- All computation done in background
- No UI blocking
- Silent failure (just log errors)
## Edge Cases Handled
### Age-Related
- Person with DOB but some photos before age 5: Filter them out
- Person without DOB: Use all photos
- Calculating age: Use precise date math
### UI States
- Banner dismissed accidentally: Access via menu
- Slideshow interrupted: Resume from beginning
- Share cancelled: Clean up temp files
### Data Issues
- Missing thumbnails: Skip that face
- Corrupted cache: Regenerate
- Face selection fails: Don't show feature
## Success Metrics
### Primary Goals
- Users express delight and share with others
- Organic growth through shared timelines
- High completion rate (>80%)
### Tracking (Anonymous)
- Feature discovery rate
- View completion percentage
- Share button usage
- Video shares completed
## Final Specifications
### Constants
```dart
const kMinYearSpan = 7;
const kPhotosPerYear = 4;
const kMinFaceScore = 0.85;
const kMinAge = 5.0; // years
const kSlideshowInterval = 2000; // ms
const kVideoFrameDuration = 1000; // ms
const kMaxConcurrentThumbnails = 4;
const kCacheValidityDays = 365;
const kThumbnailPadding = 0.4; // 40% standard
```
### Text Strings
```dart
// Banner
"How ${person.name} grew over the years"
// Menu option
"Show face timeline"
// Age display (with DOB)
"Age ${years} years${months > 0 ? ' ${months} months' : ''}"
// Relative time (without DOB)
"${years} years ago"
"${months} months ago"
"Recently"
// Share watermark
"Created with Ente Photos"
```
## Implementation Checklist
### Core Features
- [ ] Eligibility check with age filtering
- [ ] Quantile-based face selection
- [ ] JSON caching system
- [ ] Batch thumbnail generation
- [ ] View state tracking
- [ ] Banner display logic
- [ ] Menu option for viewed timelines
### Slideshow UI
- [ ] Auto-advance timer (2 seconds)
- [ ] Tap to pause/resume
- [ ] Tap sides for navigation
- [ ] Age/time display
- [ ] Close button
### Sharing Feature
- [ ] Video generation from thumbnails
- [ ] Text overlay on frames
- [ ] Watermark addition
- [ ] System share sheet integration
- [ ] Temp file cleanup
## Questions Resolved
1. **Face selection**: Quantile approach (1st, 25th, 50th, 75th) ✓
2. **Banner behavior**: Show once until viewed ✓
3. **Controls**: Tap to pause, sides to navigate ✓
4. **Age filtering**: Exclude ≤4 years old ✓
5. **Face cropping**: Use standard padding ✓
6. **Cache duration**: 1 year ✓
7. **Loading**: No indicators, silent generation ✓
8. **Sharing**: Simple video export ✓
## Ready for Implementation
This design is now complete and ready for implementation. The MVP balances simplicity with user delight, includes viral sharing potential, and leverages existing infrastructure efficiently.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
# Faces Through Time - Implementation Progress
## Implementation Checklist
### Phase 1: Database & Models
- [x] Create FaceTimeline model class
- [x] Create FaceTimelineEntry model class
- [x] Add getPersonFileIds query to MLDataDB
- [x] Add getPersonFacesWithScores query to MLDataDB
- [ ] Test database queries
### Phase 2: Core Service
- [x] Create FacesThroughTimeService class
- [x] Implement eligibility checking logic
- [x] Implement quantile-based face selection
- [x] Implement JSON caching mechanism
- [x] Add view state tracking with SharedPreferences
### Phase 3: Thumbnail Generation
- [ ] Integrate with existing face thumbnail cache
- [ ] Implement batch processing (4 at a time)
- [ ] Add memory management for thumbnails
- [ ] Test thumbnail generation
### Phase 4: UI Components
- [x] Create FacesTimelineBanner widget
- [x] Create FacesThroughTimePage (slideshow)
- [x] Implement auto-advance timer (2 seconds)
- [x] Add tap controls (pause/resume/navigate)
- [x] Add age/time display logic
### Phase 5: Integration
- [x] Create FacesTimelineReadyEvent
- [x] Integrate with PeoplePage
- [x] Add banner display logic
- [ ] Add menu option for viewed timelines
- [x] Test end-to-end flow
### Phase 6: Video Generation
- [ ] Create FacesThroughTimeVideoService
- [ ] Implement FFmpeg video generation
- [ ] Add text overlays for age/time
- [ ] Add Ente watermark
- [ ] Integrate with system share sheet
### Phase 7: Testing & Polish
- [ ] Test eligibility with various photo counts
- [ ] Verify age filtering (exclude ≤4 years)
- [ ] Test video generation and sharing
- [ ] Performance optimization
- [ ] Error handling for edge cases
## Current Status
Starting implementation...
## Notes
- Following design doc: FACES_THROUGH_TIME_DESIGN_V2.md
- Following implementation guide: FACES_THROUGH_TIME_IMPLEMENTATION_COMPLETE.md

View File

@@ -1,36 +1,36 @@
ente is a simple app to backup and share your photos and videos.
ente je jednoduchá aplikace pro automatické zálohování a organizaci vašich fotek a videí.
If you've been looking for a privacy-friendly alternative to Google Photos, you've come to the right place. With ente, they are stored end-to-end encrypted (e2ee). This means that only you can view them.
Pokud hledáte alternativu ke službě Google Photos, která respektuje vaše soukromí, jste na správném místě. S Ente jsou uloženy s koncovým "end-to-end" šifrováním (e2ee). To znamená, že je můžete vidět pouze vy.
We have open-source apps across Android, iOS, web and desktop, and your photos will seamlessly sync between all of them in an end-to-end encrypted (e2ee) manner.
Máme aplikace napříč všemi platformami a vaše fotky se budou bezproblémově synchronizovat mezi všemi vašimi zařízeními tak, aby byly koncově šifrovány mezi jednotlivými zařízeními (e2ee).
ente also makes it simple to share your albums with your loved ones, even if they aren't on ente. You can share publicly viewable links, where they can view your album and collaborate by adding photos to it, even without an account or app.
ente také usnadňuje sdílení vašich alb s vašimi blízkými, i když nejsou na ente. Můžete sdílet veřejně přístupné odkazy, kde si mohou prohlížet vaše album a spolupracovat přidáváním fotografií, a to i bez účtu nebo aplikace.
Your encrypted data is replicated to 3 different locations, including a fall-out shelter in Paris. We take posterity seriously and make it easy to ensure that your memories outlive you.
Vaše šifrovaná data jsou uložena na 3 místech, včetně protiatomového krytu v Paříži. Bereme budoucnost vážně a usnadňujeme vám zajistit, aby vaše vzpomínky přežily vás samotné.
We are here to make the safest photos app ever, come join our journey!
Jsme tu, abychom vytvořili nejbezpečnější aplikaci pro fotografie, jaká kdy byla. Přidejte se k nám!
FEATURES
- Original quality backups, because every pixel is important
- Family plans, so you can share storage with your family
- Collaborative albums, so you can pool together photos after a trip
- Shared folders, in case you want your partner to enjoy your "Camera" clicks
- Album links, that can be protected with a password
- Ability to free up space, by removing files that have been safely backed up
- Human support, because you're worth it
- Descriptions, so you can caption your memories and find them easily
- Image editor, to add finishing touches
- Favorite, hide and relive your memories, for they are precious
- One-click import from Google, Apple, your hard drive and more
- Dark theme, because your photos look good in it
- 2FA, 3FA, biometric auth
- and a LOT more!
VLASTNOSTI
- Zálohování v původní kvalitě, protože každý pixel je důležitý
- Rodinné plány, takže můžete sdílet úložiště s rodinou
- Společná alba, do kterých můžete po cestě shromažďovat fotografie
- Sdílené složky, aby i váš partner mohl obdivovat vaše fotografie
- Odkazy na alba, které lze chránit heslem a nastavit jejich platnost
- Možnost uvolnit místo odstraněním souborů, které byly bezpečně zálohovány
- Lidská podpora, protože si to zasloužíte
- Popisy, abyste mohli své vzpomínky opatřit popisky a snadno je najít
- Editor obrázků pro doladění detailů
- Označte si své oblíbené fotografie, skryjte je a nebo je prožijte znovu pomocí vzpomínek
- Import jedním kliknutím z Google, Apple, pevného disku a dalších zdrojů
- Tmavý motiv, protože vaše fotografie v něm vypadají dobře
- 2FA, 3FA, biometrické ověření
- a ještě MNOHEM víc!
OPRÁVNĚNÍ
ente requests for certain permissions to serve the purpose of a photo storage provider, which can be reviewed here: https://github.com/ente-io/ente/blob/f-droid/mobile/android/permissions.md
ente žádá o určitá oprávnění, aby mohla plnit funkci poskytovatele úložiště fotografií. Tyto oprávnění si můžete prohlédnout zde: https://github.com/ente-io/ente/blob/f-droid/mobile/android/permissions.md
PRICING
We don't offer forever free plans, because it is important to us that we remain sustainable and withstand the test of time. Instead we offer affordable plans that you can freely share with your family. You can find more information at ente.io.
CENY
Nenabízíme doživotní tarify, protože je pro nás důležité zůstat udržitelnými a obstát ve zkoušce času. Místo toho nabízíme cenově dostupné plány, které můžete svobodně sdílet se svou rodinou. Více informací najdete na ente.io.
PODPORA
We take pride in offering human support. If you are our paid customer, you can reach out to team@ente.io and expect a response from our team within 24 hours.
Jsme hrdí na to, že poskytujeme lidskou podporu. Pokud máte placený tarif, můžete se obrátit na team@ente.io a do 24 hodin od našeho týmu očekávat odpověď.

View File

@@ -1 +1 @@
ente is an end-to-end encrypted photo storage app
ente je aplikace pro ukládání fotografií a videí s koncovým šifrováním

View File

@@ -1 +1 @@
ente - encrypted photo storage
ente šifrované úložiště fotografií

View File

@@ -1,4 +1,4 @@
ente es una aplicación simple para hacer copias de seguridad y compartir tus fotos y videos.
ente es una aplicación simple para hacer copias de seguridad y compartir tus fotos y vídeos.
Si has estado buscando una alternativa a Google Photos que sea amigable con la privacidad, has llegado al lugar correcto. Con Ente, se almacenan cifradas de extremo a extremo (e2ee). Esto significa que solo tú puedes verlas.

View File

@@ -6,23 +6,23 @@ Kami menyediakan app untuk Android, iOS, web, serta desktop, dan fotomu akan ter
ente juga dapat memudahkan kamu untuk membagikan album ke orang tersayang, meski mereka tidak punya akun ente. Kamu dapat membagikan link berbagi publik, di mana mereka bisa melihat album kamu dan berkolaborasi dengan menambahkan foto, tanpa akun atau app.
Data terenkripsi kamu tersimpan di 3 lokasi berbeda, termasuk di salah satu tempat pengungsian di Paris. We take posterity seriously and make it easy to ensure that your memories outlive you.
Data terenkripsi kamu tersimpan di 3 lokasi berbeda, termasuk di salah satu tempat pengungsian di Paris. Kami menanggapi keturunan anda dengan serius dan memudahkan untuk memastikan kenangan anda tetap ada setelah anda.
Kami ingin membuat app foto yang paling aman sepanjang masajadi, bergabunglah dengan kami!
FITUR
- Pencadangan kualitas asli, karena setiap piksel berarti
- Paket keluarga, sehingga kamu bisa bagikan kuota penyimpananmu dengan keluarga
- Collaborative albums, so you can pool together photos after a trip
- Shared folders, in case you want your partner to enjoy your "Camera" clicks
- Album kolaboratif, sehingga anda dapat mengumpulkan foto bersama setelah sebuah perjalanan
- Folder bersama, jika anda ingin pasangan anda menikmati hasil jepretan "Kamera" anda
- Link album, yang bisa dilindungi dengan sandi
Kemampuan untuk membebaskan kapasitas, dengan menghilangkan files yang sudah di back-up dengan aman
- Human support, because you're worth it
- Descriptions, so you can caption your memories and find them easily
- Dukungan manusia, karena anda layak mendapatkannya
- Deskripsi, sehingga anda dapat memberi keterangan pada memori anda dan menemukannya dengan mudah
- Editor gambar, untuk menyempurnakan fotomu
- Favorite, hide and relive your memories, for they are precious
- Favoritkan, sembunyikan, dan kenang kembali memori anda, karena itu sangat berharga
- Pengimporan mudah dari Google, Apple, hard drive-mu, dan lainnya
- Dark theme, because your photos look good in it
- Tema gelap, karena foto anda terlihat bagus di dalamnya
- Autentikasi dua atau tiga faktor dan autentikasi biometrik
- dan BANYAK LAGI!

View File

@@ -1,33 +1,33 @@
Ente is a simple app to automatically backup and organize your photos and videos.
Ente je jednoduchá aplikace pro automatické zálohování a organizaci vašich fotek a videí.
If you've been looking for a privacy-friendly alternative to preserve your memories, you've come to the right place. With Ente, they are stored end-to-end encrypted (e2ee). This means that only you can view them.
Pokud hledáte alternativu, která respektuje vaše soukromí a umožní vám uchovat vaše vzpomínky, jste na správném místě. S Ente jsou uloženy s koncovým "end-to-end" šifrováním (e2ee). To znamená, že je můžete vidět pouze vy.
We have apps across all platforms, and your photos will seamlessly sync between all your devices in an end-to-end encrypted (e2ee) manner.
Máme aplikace napříč všemi platformami a vaše fotky se budou bezproblémově synchronizovat mezi všemi vašimi zařízeními tak, aby byly koncově šifrovány mezi jednotlivými zařízeními (e2ee).
Ente also makes it simple to share your albums with your loved ones. You can either share them directly with other Ente users, end-to-end encrypted; or with publicly viewable links.
Ente také zjednodušuje sdílení alb s vašimi blízkými. Můžete je buď sdílet přímo s ostatními uživateli Ente, koncově šifrované; nebo s pomocí veřejně přístupných odkazů.
Your encrypted data is stored across multiple locations, including a fall-out shelter in Paris. We take posterity seriously and make it easy to ensure that your memories outlive you.
Vaše šifrovaná data jsou uložena na několika místech, včetně protiatomového krytu v Paříži. Bereme budoucnost vážně a usnadňujeme vám zajistit, aby vaše vzpomínky přežily vás samotné.
We are here to make the safest photos app ever, come join our journey!
Jsme tu, abychom vytvořili nejbezpečnější aplikaci pro fotografie, jaká kdy byla. Přidejte se k nám!
FEATURES
- Original quality backups, because every pixel is important
- Family plans, so you can share storage with your family
- Shared folders, in case you want your partner to enjoy your "Camera" clicks
- Album links, that can be protected with a password and set to expire
- Ability to free up space, by removing files that have been safely backed up
- Image editor, to add finishing touches
- Favorite, hide and relive your memories, for they are precious
- One-click import from all major storage providers
- Dark theme, because your photos look good in it
- 2FA, 3FA, biometric auth
- and a LOT more!
VLASTNOSTI
- Zálohování v původní kvalitě, protože každý pixel je důležitý
- Rodinné plány, takže můžete sdílet úložiště s rodinou
- Sdílené složky, aby i váš partner mohl obdivovat vaše fotografie
- Odkazy na alba, které lze chránit heslem a nastavit jejich platnost
- Možnost uvolnit místo odstraněním souborů, které byly bezpečně zálohovány
- Editor obrázků, pro finální doladění detailů
- Označte si své oblíbené fotografie, skryjte je a nebo je prožijte znovu pomocí vzpomínek
- Import jedním kliknutím ze všech velkých poskytovatelů úložišť
- Tmavý motiv, protože vaše fotografie v něm vypadají dobře
- 2FA, 3FA, biometrické ověření
- a ještě MNOHEM víc!
PRICING
We don't offer forever free plans, because it is important to us that we remain sustainable and withstand the test of time. Instead we offer affordable plans that you can freely share with your family. You can find more information at ente.io.
CENY
Nenabízíme doživotní tarify, protože je pro nás důležité zůstat udržitelnými a obstát ve zkoušce času. Místo toho nabízíme cenově dostupné plány, které můžete svobodně sdílet se svou rodinou. Více informací najdete na ente.io.
SUPPORT
We take pride in offering human support. If you are our paid customer, you can reach out to team@ente.io and expect a response from our team within 24 hours.
PODPORA
Jsme hrdí na to, že poskytujeme lidskou podporu. Pokud máte placený tarif, můžete se obrátit na team@ente.io a do 24 hodin od našeho týmu očekávat odpověď.
TERMS
PODMÍNKY
https://ente.io/terms

View File

@@ -1 +1 @@
photos,photography,family,privacy,cloud,backup,videos,photo,encryption,storage,album,alternative
fotky,fotografie,rodina,soukromí,cloud,zálohování,videa,fotka,šifrování,úložiště,album,alternativa

View File

@@ -1 +1 @@
Ente Photos
Ente Fotky

View File

@@ -1 +1 @@
Encrypted photo storage
Šifrované úložiště fotografií

View File

@@ -6,20 +6,20 @@ Kami menyediakan app untuk semua platform, dan fotomu akan tersinkron di semua p
Ente juga memudahkan kamu untuk membagikan album ke kerabatmu. Kamu bisa membagikannya secara langsung ke pengguna Ente lain, terenkripsi ujung ke ujung; atau dengan link yang dapat dilihat publik.
Data terenkripsi kamu tersimpan di berbagai lokasi, termasuk di salah satu tempat pengungsian di Paris. We take posterity seriously and make it easy to ensure that your memories outlive you.
Data terenkripsi kamu tersimpan di berbagai lokasi, termasuk di salah satu tempat pengungsian di Paris. Kami menanggapi keturunan anda dengan serius dan memudahkan untuk memastikan kenangan anda tetap ada setelah anda.
Kami ingin membuat app foto yang paling aman sepanjang masajadi, bergabunglah dengan kami!
FITUR
- Pencadangan kualitas asli, karena setiap piksel berarti
- Paket keluarga, sehingga kamu bisa bagikan kuota penyimpananmu dengan keluarga
- Shared folders, in case you want your partner to enjoy your "Camera" clicks
- Folder bersama, jika anda ingin pasangan anda menikmati hasil jepretan "Kamera" anda
- Link album, yang bisa dilindungi dengan sandi dan diatur waktu kedaluwarsanya
- Ability to free up space, by removing files that have been safely backed up
- Kemampuan untuk mengosongkan ruang, dengan menghapus file yang telah dicadangkan dengan aman
- Editor gambar, untuk menyempurnakan fotomu
- Favorite, hide and relive your memories, for they are precious
- One-click import from all major storage providers
- Dark theme, because your photos look good in it
Favoritkan, sembunyikan, dan kenang kembali memori anda, karena itu sangat berharga
Impor sekali klik dari semua penyedia penyimpanan utama
Tema gelap, karena foto anda terlihat bagus di dalamnya
- Autentikasi dua atau tiga faktor dan autentikasi biometrik
- dan BANYAK LAGI!
@@ -27,7 +27,7 @@ HARGA
Kami tidak menyediakan paket yang gratis seumur hidup, karena penting bagi kami untuk tetap berdiri dan bertahan hingga masa depan. Namun, kami menyediakan paket yang terjangkau, yang bisa kamu bagikan dengan keluargamu. Kamu bisa menemukan informasi lebih lanjut di ente.io.
DUKUNGAN
We take pride in offering human support. Jika kamu adalah pelanggan berbayar, kamu bisa menghubungi team@ente.io dan menunggu balasan dari tim kami dalam 24 jam.
Kami bangga dapat menawarkan dukungan manusia. Jika kamu adalah pelanggan berbayar, kamu bisa menghubungi team@ente.io dan menunggu balasan dari tim kami dalam 24 jam.
KETENTUAN
https://ente.io/terms

View File

@@ -1,30 +1,30 @@
Ente is a simple app to automatically backup and organize your photos and videos.
Ente je jednoduchá aplikace pro automatické zálohování a organizaci vašich fotek a videí.
If you've been looking for a privacy-friendly alternative to preserve your memories, you've come to the right place. With Ente, they are stored end-to-end encrypted (e2ee). This means that only you can view them.
Pokud hledáte alternativu, která respektuje vaše soukromí a umožní vám uchovat vaše vzpomínky, jste na správném místě. S Ente jsou uloženy s koncovým "end-to-end" šifrováním (e2ee). To znamená, že je můžete vidět pouze vy.
We have apps across Android, iOS, web and Desktop, and your photos will seamlessly sync between all your devices in an end-to-end encrypted (e2ee) manner.
Máme aplikace napříč všemi platformami a vaše fotky se budou bezproblémově synchronizovat mezi všemi vašimi zařízeními tak, aby byly koncově šifrovány mezi jednotlivými zařízeními (e2ee).
Ente also makes it simple to share your albums with your loved ones. You can either share them directly with other Ente users, end-to-end encrypted; or with publicly viewable links.
Ente také zjednodušuje sdílení alb s vašimi blízkými. Můžete je buď sdílet přímo s ostatními uživateli Ente, koncově šifrované; nebo s pomocí veřejně přístupných odkazů.
Your encrypted data is stored across multiple locations, including a fall-out shelter in Paris. We take posterity seriously and make it easy to ensure that your memories outlive you.
Vaše šifrovaná data jsou uložena na několika místech, včetně protiatomového krytu v Paříži. Bereme budoucnost vážně a usnadňujeme vám zajistit, aby vaše vzpomínky přežily vás samotné.
We are here to make the safest photos app ever, come join our journey!
Jsme tu, abychom vytvořili nejbezpečnější aplikaci pro fotografie, jaká kdy byla. Přidejte se k nám!
FEATURES
- Original quality backups, because every pixel is important
- Family plans, so you can share storage with your family
- Shared folders, in case you want your partner to enjoy your "Camera" clicks
- Album links, that can be protected with a password and set to expire
- Ability to free up space, by removing files that have been safely backed up
- Image editor, to add finishing touches
- Favorite, hide and relive your memories, for they are precious
- One-click import from Google, Apple, your hard drive and more
- Dark theme, because your photos look good in it
- 2FA, 3FA, biometric auth
- and a LOT more!
VLASTNOSTI
- Zálohování v původní kvalitě, protože každý pixel je důležitý
- Rodinné plány, takže můžete sdílet úložiště s rodinou
- Sdílené složky, aby i váš partner mohl obdivovat vaše fotografie
- Odkazy na alba, které lze chránit heslem a nastavit jejich platnost
- Možnost uvolnit místo odstraněním souborů, které byly bezpečně zálohovány
- Editor obrázků, pro finální doladění detailů
- Označte si své oblíbené fotografie, skryjte je a nebo je prožijte znovu pomocí vzpomínek
- Import jedním kliknutím ze všech velkých poskytovatelů úložišť
- Tmavý motiv, protože vaše fotografie v něm vypadají dobře
- 2FA, 3FA, biometrické ověření
- a ještě MNOHEM víc!
💲 PRICING
We don't offer forever free plans, because it is important to us that we remain sustainable and withstand the test of time. Instead we offer affordable plans that you can freely share with your family. You can find more information at ente.io.
💲 CENY
Nenabízíme doživotní tarify, protože je pro nás důležité zůstat udržitelnými a obstát ve zkoušce času. Místo toho nabízíme cenově dostupné plány, které můžete svobodně sdílet se svou rodinou. Více informací najdete na ente.io.
🙋 SUPPORT
We take pride in offering human support. If you are our paid customer, you can reach out to team@ente.io and expect a response from our team within 24 hours.
🙋 PODPORA
Jsme hrdí na to, že poskytujeme lidskou podporu. Pokud máte placený tarif, můžete se obrátit na team@ente.io a do 24 hodin od našeho týmu očekávat odpověď.

View File

@@ -1 +1 @@
Encrypted photo storage - backup, organize and share your photos and videos
Šifrované úložiště fotografií zálohujte, organizujte a sdílejte své fotografie a videa

View File

@@ -1 +1 @@
Ente Photos
Ente Fotky

View File

@@ -6,20 +6,20 @@ Kami menyediakan app untuk Android, iOS, Web, serta Desktop, dan fotomu akan ter
Ente juga memudahkan kamu untuk membagikan album ke kerabatmu. Kamu bisa membagikannya secara langsung ke pengguna Ente lain, terenkripsi ujung ke ujung; atau dengan link yang dapat dilihat publik.
Data terenkripsi kamu tersimpan di berbagai lokasi, termasuk di salah satu tempat pengungsian di Paris. We take posterity seriously and make it easy to ensure that your memories outlive you.
Data terenkripsi kamu tersimpan di berbagai lokasi, termasuk di salah satu tempat pengungsian di Paris. Kami menanggapi keturunan anda dengan serius dan memudahkan untuk memastikan kenangan anda tetap ada setelah anda.
Kami ingin membuat app foto yang paling aman sepanjang masajadi, bergabunglah dengan kami!
✨ FITUR
- Pencadangan kualitas asli, karena setiap piksel berarti
- Paket keluarga, sehingga kamu bisa bagikan kuota penyimpananmu dengan keluarga
- Shared folders, in case you want your partner to enjoy your "Camera" clicks
- Folder bersama, jika anda ingin pasangan anda menikmati hasil jepretan "Kamera" anda
- Link album, yang bisa dilindungi dengan sandi dan diatur waktu kedaluwarsanya
- Ability to free up space, by removing files that have been safely backed up
- Kemampuan untuk mengosongkan ruang, dengan menghapus file yang telah dicadangkan dengan aman
- Editor gambar, untuk menyempurnakan fotomu
- Favorite, hide and relive your memories, for they are precious
- Favoritkan, sembunyikan, dan kenang kembali memori anda, karena itu sangat berharga
- Pengimporan mudah dari Google, Apple, hard drive-mu, dan lainnya
- Dark theme, because your photos look good in it
- Tema gelap, karena foto anda terlihat bagus di dalamnya
- Autentikasi dua atau tiga faktor dan autentikasi biometrik
- dan BANYAK LAGI!
@@ -27,4 +27,4 @@ Kami ingin membuat app foto yang paling aman sepanjang masajadi, bergabunglah
Kami tidak menyediakan paket yang gratis seumur hidup, karena penting bagi kami untuk tetap berdiri dan bertahan hingga masa depan. Namun, kami menyediakan paket yang terjangkau, yang bisa kamu bagikan dengan keluargamu. Kamu bisa menemukan informasi lebih lanjut di ente.io.
🙋 DUKUNGAN
We take pride in offering human support. Jika kamu adalah pelanggan berbayar, kamu bisa menghubungi team@ente.io dan menunggu balasan dari tim kami dalam 24 jam.
Kami bangga dapat menawarkan dukungan manusia. Jika kamu adalah pelanggan berbayar, kamu bisa menghubungi team@ente.io dan menunggu balasan dari tim kami dalam 24 jam.

View File

@@ -181,6 +181,8 @@ PODS:
- PromisesObjC (2.4.0)
- receive_sharing_intent (1.8.1):
- Flutter
- rive_common (0.0.1):
- Flutter
- rust_lib_photos (0.0.1):
- Flutter
- SDWebImage (5.21.1):
@@ -291,6 +293,7 @@ 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`)
- rive_common (from `.symlinks/plugins/rive_common/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`)
@@ -418,6 +421,8 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/privacy_screen/ios"
receive_sharing_intent:
:path: ".symlinks/plugins/receive_sharing_intent/ios"
rive_common:
:path: ".symlinks/plugins/rive_common/ios"
rust_lib_photos:
:path: ".symlinks/plugins/rust_lib_photos/ios"
sentry_flutter:
@@ -510,6 +515,7 @@ SPEC CHECKSUMS:
privacy_screen: 3159a541f5d3a31bea916cfd4e58f9dc722b3fd4
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
receive_sharing_intent: 222384f00ffe7e952bbfabaa9e3967cb87e5fe00
rive_common: dd421daaf9ae69f0125aa761dd96abd278399952
rust_lib_photos: 04d3901908d2972192944083310b65abf410774c
SDWebImage: f29024626962457f3470184232766516dee8dfea
SDWebImageWebPCoder: e38c0a70396191361d60c092933e22c20d5b1380

View File

@@ -565,6 +565,7 @@
"${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}/rive_common/rive_common.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",
@@ -662,6 +663,7 @@
"${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}/rive_common.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",

View File

@@ -0,0 +1,38 @@
{
"images" : [
{
"filename" : "IconDuckyHuggingEAny.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"filename" : "IconDuckyHuggingEDark.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "tinted"
}
],
"filename" : "IconDuckyHuggingETinted.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

View File

@@ -142,7 +142,7 @@ class _EnteAppState extends State<EnteApp> with WidgetsBindingObserver {
debugShowCheckedModeBanner: false,
builder: EasyLoading.init(),
locale: locale,
supportedLocales: AppLocalizations.supportedLocales,
supportedLocales: appSupportedLocales,
localeListResolutionCallback: localResolutionCallBack,
localizationsDelegates: const [
...AppLocalizations.localizationsDelegates,
@@ -164,7 +164,7 @@ class _EnteAppState extends State<EnteApp> with WidgetsBindingObserver {
debugShowCheckedModeBanner: false,
builder: EasyLoading.init(),
locale: locale,
supportedLocales: AppLocalizations.supportedLocales,
supportedLocales: appSupportedLocales,
localeListResolutionCallback: localResolutionCallBack,
localizationsDelegates: const [
...AppLocalizations.localizationsDelegates,

View File

@@ -28,6 +28,7 @@ import 'package:photos/services/favorites_service.dart';
import "package:photos/services/home_widget_service.dart";
import 'package:photos/services/ignored_files_service.dart';
import "package:photos/services/machine_learning/face_ml/person/person_service.dart";
import "package:photos/services/machine_learning/similar_images_service.dart";
import 'package:photos/services/search_service.dart';
import 'package:photos/services/sync/sync_service.dart';
import 'package:photos/utils/file_uploader.dart';
@@ -196,6 +197,7 @@ class Configuration {
await CollectionsDB.instance.clearTable();
await MemoriesDB.instance.clearTable();
await MLDataDB.instance.clearTable();
await SimilarImagesService.instance.clearCache();
await UploadLocksDB.instance.clearTable();
await IgnoredFilesService.instance.reset();

View File

@@ -0,0 +1,13 @@
// Common runtime exceptions that can occur during normal app operation.
// These are recoverable conditions that should be caught and handled.
class WidgetUnmountedException implements Exception {
final String? message;
WidgetUnmountedException([this.message]);
@override
String toString() => message != null
? 'WidgetUnmountedException: $message'
: 'WidgetUnmountedException';
}

View File

@@ -981,7 +981,8 @@ class FilesDB with SqlDbBase {
// remove references for local files which are either already uploaded
// or queued for upload but not yet uploaded
Future<int> removeQueuedLocalFiles(Set<String> localIDs) async {
// Remove queued local files that have duplicate uploaded entries with same localID
Future<int> removeQueuedLocalFiles(Set<String> localIDs, int ownerID) async {
if (localIDs.isEmpty) {
_logger.finest("No local IDs provided for removal");
return 0;
@@ -990,54 +991,63 @@ class FilesDB with SqlDbBase {
final db = await instance.sqliteAsyncDB;
const batchSize = 10000;
int totalRemoved = 0;
final localIDsList = localIDs.toList();
for (int i = 0; i < localIDsList.length; i += batchSize) {
final endIndex = (i + batchSize > localIDsList.length)
? localIDsList.length
: i + batchSize;
final batch = localIDsList.sublist(i, endIndex);
final placeholders = List.filled(batch.length, '?').join(',');
final List<String> alreadyUploaded = [];
// find localIDs that are already uploaded
final result = await db.execute('''
// Find localIDs that already have uploaded entries
final result = await db.execute(
'''
SELECT DISTINCT $columnLocalID
FROM $filesTable
WHERE $columnLocalID IN ($placeholders)
WHERE
$columnOwnerID = $ownerID
AND $columnLocalID IN ($placeholders)
AND ($columnUploadedFileID IS NOT NULL AND $columnUploadedFileID != -1)
''');
for (final row in result) {
alreadyUploaded.add(row[columnLocalID] as String);
}
final uploadedPlaceholders =
alreadyUploaded.map((id) => "'$id'").join(',');
final r = await db.execute(
'''
DELETE FROM $filesTable
WHERE $columnLocalID IN ($uploadedPlaceholders)
AND ($columnUploadedFileID IS NULL OR $columnUploadedFileID = -1)
''',
''',
batch,
);
if (r.isNotEmpty) {
_logger.warning(
"Batch ${(i ~/ batchSize) + 1}: Removed duplicate ${r.length} files",
if (result.isNotEmpty) {
final alreadyUploadedLocalIDs =
result.map((row) => row[columnLocalID] as String).toList();
final localIdPlaceholder =
List.filled(alreadyUploadedLocalIDs.length, '?').join(',');
// Delete queued entries for localIDs that already have uploaded versions
final deleteResult = await db.execute(
'''
DELETE FROM $filesTable
WHERE $columnLocalID IN ($localIdPlaceholder)
AND ($columnUploadedFileID IS NULL OR $columnUploadedFileID = -1)
''',
alreadyUploadedLocalIDs,
);
totalRemoved += r.length;
final removedCount =
deleteResult.length; // or however your DB returns affected rows
if (removedCount > 0) {
_logger.warning(
"Batch ${(i ~/ batchSize) + 1}: Removed $removedCount queued duplicates",
);
totalRemoved += removedCount;
}
}
}
if (totalRemoved > 0) {
_logger.warning(
"Removed $totalRemoved potential dups for already queued local files",
"Removed $totalRemoved queued files that had uploaded duplicates",
);
} else {
_logger.finest("No duplicate id found for queued/uploaded files");
_logger.finest("No queued duplicates found for uploaded files");
}
return totalRemoved;
}
@@ -1677,26 +1687,36 @@ class FilesDB with SqlDbBase {
);
}
Future<List<EnteFile>> getAllFilesAfterDate({
required FileType fileType,
required DateTime beginDate,
Future<List<EnteFile>> getStreamingEligibleVideoFiles({
DateTime? beginDate,
required int userID,
bool onlyFilesWithLocalId = false,
}) async {
final db = await instance.sqliteAsyncDB;
final results = await db.getAll(
'''
String query = '''
SELECT * FROM $filesTable
WHERE $columnFileType = ?
AND $columnCreationTime > ?
AND $columnUploadedFileID != -1
AND $columnOwnerID = $userID
AND $columnLocalID IS NOT NULL
AND ($columnUploadedFileID IS NOT NULL AND $columnUploadedFileID != -1)
AND $columnOwnerID = ?
AND ($columnFileSize IS NOT NULL AND $columnFileSize <= 524288000)
AND ($columnDuration IS NOT NULL AND ($columnDuration <= 60 AND $columnDuration > 0))
ORDER BY $columnCreationTime DESC
''',
[getInt(fileType), beginDate.microsecondsSinceEpoch],
);
''';
final List<Object> queryArgs = [getInt(FileType.video), userID];
if (beginDate != null) {
query += ' AND $columnCreationTime > ?';
queryArgs.add(beginDate.microsecondsSinceEpoch);
}
if (onlyFilesWithLocalId) {
query += ' AND $columnLocalID IS NOT NULL';
}
query += ' ORDER BY $columnCreationTime DESC';
final results = await db.getAll(query, queryArgs);
return convertToFiles(results);
}

View File

@@ -8,11 +8,13 @@ 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";
import "package:shared_preferences/shared_preferences.dart";
class ClipVectorDB {
static final Logger _logger = Logger("ClipVectorDB");
static const _databaseName = "ente.ml.vectordb.clip";
static const _databaseName = "ente.ml.vectordb.clip.usearch";
static const _kMigrationKey = "clip_vectordb_migration";
static final BigInt _embeddingDimension = BigInt.from(512);
@@ -35,11 +37,10 @@ class ClipVectorDB {
Future<VectorDb> _initVectorDB() async {
final documentsDirectory = await getApplicationDocumentsDirectory();
final String databaseDirectory =
join(documentsDirectory.path, _databaseName);
_logger.info("Opening vectorDB access: DB path " + databaseDirectory);
final String dbPath = join(documentsDirectory.path, _databaseName);
_logger.info("Opening vectorDB access: DB path " + dbPath);
final vectorDB = VectorDb(
filePath: databaseDirectory,
filePath: dbPath,
dimensions: _embeddingDimension,
);
final stats = await getIndexStats(vectorDB);
@@ -51,10 +52,9 @@ class ClipVectorDB {
Future<bool> checkIfMigrationDone() async {
if (_migrationDone != null) return _migrationDone!;
_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()) {
final prefs = await SharedPreferences.getInstance();
final migrationDone = prefs.getBool(_kMigrationKey) ?? false;
if (migrationDone) {
_logger.info("ClipVectorDB migration already done");
_migrationDone = true;
return _migrationDone!;
@@ -67,10 +67,8 @@ class ClipVectorDB {
Future<void> setMigrationDone() async {
_logger.info("Setting ClipVectorDB migration done");
final documentsDirectory = await getApplicationDocumentsDirectory();
final migrationFlagFile =
File(join(documentsDirectory.path, 'clip_vector_migration_done'));
await migrationFlagFile.create(recursive: true);
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_kMigrationKey, true);
_migrationDone = true;
}
@@ -143,17 +141,6 @@ class ClipVectorDB {
}
}
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 {
@@ -280,6 +267,40 @@ class ClipVectorDB {
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<void> deleteIndexFile({bool undoMigration = false}) async {
try {
final documentsDirectory = await getApplicationDocumentsDirectory();
final String dbPath = join(documentsDirectory.path, _databaseName);
_logger.info("Delete index file: DB path " + dbPath);
final file = File(dbPath);
if (await file.exists()) {
await file.delete();
}
_logger.info("Deleted index file on disk");
_vectorDbFuture = null;
if (undoMigration) {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_kMigrationKey, false);
_migrationDone = false;
_logger.info("Undid migration flag");
}
} catch (e, s) {
_logger.severe("Error deleting index file on disk", e, s);
rethrow;
}
}
}
class VectorDbStats {

View File

@@ -260,6 +260,9 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
await db.execute(deleteNotPersonFeedbackTable);
await db.execute(deleteClipEmbeddingsTable);
await db.execute(deleteFileDataTable);
if (await ClipVectorDB.instance.checkIfMigrationDone()) {
await ClipVectorDB.instance.deleteIndexFile();
}
}
@override
@@ -1289,8 +1292,11 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
int processedCount = 0;
int weirdCount = 0;
int whileCount = 0;
const String migrationKey = "clip_vector_db_migration_in_progress";
final stopwatch = Stopwatch()..start();
try {
// Make sure no other heavy compute is running
computeController.blockCompute(blocker: migrationKey);
while (true) {
whileCount++;
_logger.info("$whileCount st round of while loop");
@@ -1320,6 +1326,9 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
embeddings.add(Float32List.view(result[embeddingColumn].buffer));
} else {
weirdCount++;
_logger.warning(
"Weird clip embedding length ${embedding.length} for fileID ${result[fileIDColumn]}, skipping",
);
}
}
_logger.info(
@@ -1346,7 +1355,7 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
"migrated all $totalCount embeddings to ClipVectorDB in ${stopwatch.elapsed.inMilliseconds} ms, with $weirdCount weird embeddings not migrated",
);
await ClipVectorDB.instance.setMigrationDone();
_logger.info("ClipVectorDB migration done, flag file created");
_logger.info("ClipVectorDB migration done");
} catch (e, s) {
_logger.severe(
"Error migrating ClipVectorDB after ${stopwatch.elapsed.inMilliseconds} ms, clearing out DB again",
@@ -1357,6 +1366,8 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
rethrow;
} finally {
stopwatch.stop();
// Make sure compute can run again
computeController.unblockCompute(blocker: migrationKey);
}
}
@@ -1494,4 +1505,52 @@ class MLDataDB with SqlDbBase implements IMLDataDB<int> {
final List<Object?> params = [personOrClusterID];
await db.execute(sql, params);
}
// Faces Through Time feature queries
// Note: These methods need to be used in conjunction with FilesDB
// to get creation_time information, as that's stored in a different database
Future<List<int>> getPersonFileIds(String personId) async {
final db = await instance.asyncDB;
final result = await db.getAll(
'''
SELECT DISTINCT fc.$fileIDColumn
FROM $facesTable fc
JOIN $faceClustersTable fcluster ON fc.$faceIDColumn = fcluster.$faceIDColumn
JOIN $clusterPersonTable cp ON fcluster.$clusterIDColumn = cp.$clusterIDColumn
WHERE cp.$personIdColumn = ?
''',
[personId],
);
return result.map((row) => row[fileIDColumn] as int).toList();
}
Future<List<Map<String, dynamic>>> getPersonFacesWithScores(
String personId,
double minScore,
) async {
final db = await instance.asyncDB;
final results = await db.getAll(
'''
SELECT
fc.$faceIDColumn as faceId,
fc.$fileIDColumn as fileId,
fc.$faceScore as score,
fc.$faceBlur as blur
FROM $facesTable fc
JOIN $faceClustersTable fcluster ON fc.$faceIDColumn = fcluster.$faceIDColumn
JOIN $clusterPersonTable cp ON fcluster.$clusterIDColumn = cp.$clusterIDColumn
WHERE cp.$personIdColumn = ? AND fc.$faceScore >= ?
''',
[personId, minScore],
);
return results.map((row) {
return {
'faceId': row['faceId'],
'fileId': row['fileId'],
'score': row['score'],
'blur': row['blur'],
};
}).toList();
}
}

View File

@@ -53,6 +53,13 @@ class UploadLocksDB {
columnCreatedAt: "created_at",
);
static const _streamQueueTable = (
table: "stream_queue",
columnUploadedFileID: "uploaded_file_id",
columnQueueType: "queue_type", // 'create' or 'recreate'
columnCreatedAt: "created_at",
);
static final initializationScript = [
..._createUploadLocksTable(),
];
@@ -134,18 +141,26 @@ class UploadLocksDB {
${_streamUploadErrorTable.columnCreatedAt} INTEGER DEFAULT CURRENT_TIMESTAMP NOT NULL
)
''',
'''
CREATE TABLE IF NOT EXISTS ${_streamQueueTable.table} (
${_streamQueueTable.columnUploadedFileID} INTEGER PRIMARY KEY,
${_streamQueueTable.columnQueueType} TEXT NOT NULL,
${_streamQueueTable.columnCreatedAt} INTEGER DEFAULT CURRENT_TIMESTAMP NOT NULL
)
''',
];
}
Future<void> clearTable() async {
final db = await instance.database;
final db = await database;
await db.delete(_uploadLocksTable.table);
await db.delete(_trackUploadTable.table);
await db.delete(_partsTable.table);
await db.delete(_streamQueueTable.table);
}
Future<void> acquireLock(String id, String owner, int time) async {
final db = await instance.database;
final db = await database;
final row = <String, dynamic>{};
row[_uploadLocksTable.columnID] = id;
row[_uploadLocksTable.columnOwner] = owner;
@@ -158,7 +173,7 @@ class UploadLocksDB {
}
Future<String> getLockData(String id) async {
final db = await instance.database;
final db = await database;
final rows = await db.query(
_uploadLocksTable.table,
where: '${_uploadLocksTable.columnID} = ?',
@@ -175,7 +190,7 @@ class UploadLocksDB {
}
Future<bool> isLocked(String id, String owner) async {
final db = await instance.database;
final db = await database;
final rows = await db.query(
_uploadLocksTable.table,
where:
@@ -186,7 +201,7 @@ class UploadLocksDB {
}
Future<int> releaseLock(String id, String owner) async {
final db = await instance.database;
final db = await database;
return db.delete(
_uploadLocksTable.table,
where:
@@ -196,7 +211,7 @@ class UploadLocksDB {
}
Future<int> releaseLocksAcquiredByOwnerBefore(String owner, int time) async {
final db = await instance.database;
final db = await database;
return db.delete(
_uploadLocksTable.table,
where:
@@ -206,7 +221,7 @@ class UploadLocksDB {
}
Future<int> releaseAllLocksAcquiredBefore(int time) async {
final db = await instance.database;
final db = await database;
return db.delete(
_uploadLocksTable.table,
where: '${_uploadLocksTable.columnTime} < ?',
@@ -220,7 +235,7 @@ class UploadLocksDB {
String fileHash,
int collectionID,
) async {
final db = await instance.database;
final db = await database;
final rows = await db.query(
_trackUploadTable.table,
@@ -247,7 +262,7 @@ class UploadLocksDB {
String fileHash,
int collectionID,
) async {
final db = await instance.database;
final db = await database;
await db.update(
_trackUploadTable.table,
{
@@ -270,7 +285,7 @@ class UploadLocksDB {
String fileHash,
int collectionID,
) async {
final db = await instance.database;
final db = await database;
final rows = await db.query(
_trackUploadTable.table,
where: '${_trackUploadTable.columnLocalID} = ?'
@@ -328,7 +343,7 @@ class UploadLocksDB {
int uploadedFileID,
String errorMessage,
) async {
final db = await UploadLocksDB.instance.database;
final db = await database;
await db.insert(
_streamUploadErrorTable.table,
@@ -346,7 +361,7 @@ class UploadLocksDB {
int uploadedFileID,
String errorMessage,
) async {
final db = await instance.database;
final db = await database;
await db.update(
_streamUploadErrorTable.table,
{
@@ -360,7 +375,7 @@ class UploadLocksDB {
}
Future<int> deleteStreamUploadErrorEntry(int uploadedFileID) async {
final db = await instance.database;
final db = await database;
return await db.delete(
_streamUploadErrorTable.table,
where: '${_streamUploadErrorTable.columnUploadedFileID} = ?',
@@ -369,7 +384,7 @@ class UploadLocksDB {
}
Future<Map<int, String>> getStreamUploadError() {
return instance.database.then((db) async {
return database.then((db) async {
final rows = await db.query(
_streamUploadErrorTable.table,
columns: [
@@ -398,7 +413,7 @@ class UploadLocksDB {
String keyNonce, {
required int partSize,
}) async {
final db = await UploadLocksDB.instance.database;
final db = await database;
final objectKey = urls.objectKey;
await db.insert(
@@ -441,7 +456,7 @@ class UploadLocksDB {
int partNumber,
String etag,
) async {
final db = await instance.database;
final db = await database;
await db.update(
_partsTable.table,
{
@@ -458,7 +473,7 @@ class UploadLocksDB {
String objectKey,
MultipartStatus status,
) async {
final db = await instance.database;
final db = await database;
await db.update(
_trackUploadTable.table,
{
@@ -472,7 +487,7 @@ class UploadLocksDB {
Future<int> deleteMultipartTrack(
String localId,
) async {
final db = await instance.database;
final db = await database;
return await db.delete(
_trackUploadTable.table,
where: '${_trackUploadTable.columnLocalID} = ?',
@@ -482,7 +497,7 @@ class UploadLocksDB {
// getFileNameToLastAttemptedAtMap returns a map of encrypted file name to last attempted at time
Future<Map<String, int>> getFileNameToLastAttemptedAtMap() {
return instance.database.then((db) async {
return database.then((db) async {
final rows = await db.query(
_trackUploadTable.table,
columns: [
@@ -504,7 +519,7 @@ class UploadLocksDB {
String fileHash,
int collectionID,
) {
return instance.database.then((db) async {
return database.then((db) async {
final rows = await db.query(
_trackUploadTable.table,
where: '${_trackUploadTable.columnLocalID} = ?'
@@ -519,4 +534,56 @@ class UploadLocksDB {
return row[_trackUploadTable.columnEncryptedFileName] as String;
});
}
// Stream Queue Management Methods
Future<void> addToStreamQueue(
int uploadedFileID,
String queueType, // 'create' or 'recreate'
) async {
final db = await database;
await db.insert(
_streamQueueTable.table,
{
_streamQueueTable.columnUploadedFileID: uploadedFileID,
_streamQueueTable.columnQueueType: queueType,
},
conflictAlgorithm: ConflictAlgorithm.replace,
);
}
Future<void> removeFromStreamQueue(int uploadedFileID) async {
final db = await database;
await db.delete(
_streamQueueTable.table,
where: '${_streamQueueTable.columnUploadedFileID} = ?',
whereArgs: [uploadedFileID],
);
}
Future<Map<int, String>> getStreamQueue() async {
final db = await database;
final rows = await db.query(
_streamQueueTable.table,
columns: [
_streamQueueTable.columnUploadedFileID,
_streamQueueTable.columnQueueType,
],
);
final map = <int, String>{};
for (final row in rows) {
map[row[_streamQueueTable.columnUploadedFileID] as int] =
row[_streamQueueTable.columnQueueType] as String;
}
return map;
}
Future<bool> isInStreamQueue(int uploadedFileID) async {
final db = await database;
final rows = await db.query(
_streamQueueTable.table,
where: '${_streamQueueTable.columnUploadedFileID} = ?',
whereArgs: [uploadedFileID],
);
return rows.isNotEmpty;
}
}

View File

@@ -0,0 +1,5 @@
class FacesTimelineReadyEvent {
final String personId;
FacesTimelineReadyEvent(this.personId);
}

View File

@@ -0,0 +1,12 @@
import "package:photos/events/event.dart";
import "package:photos/models/preview/preview_item_status.dart";
class VideoPreviewStateChangedEvent extends Event {
final int fileId;
final PreviewItemStatus status;
VideoPreviewStateChangedEvent(this.fileId, this.status);
@override
String get reason => '$runtimeType: fileId=$fileId, status=$status';
}

View File

@@ -1776,11 +1776,6 @@
"same": "نفس",
"different": "مختلف",
"sameperson": "نفس الشخص؟",
"cLTitle1": "محرر الصور المتقدم",
"cLDesc1": "نحن بصدد إطلاق محرر صور جديد ومتقدم يضيف المزيد من إطارات الاقتصاص، والإعدادات المسبقة للفلاتر من أجل تعديلات سريعة، وخيارات الضبط الدقيق التي تشمل التشبع، والتباين، والسطوع، ودرجة الحرارة، وغير ذلك الكثير. يتضمن المحرر الجديد أيضا القدرة على الرسم على صورك وإضافة الرموز التعبيرية كملصقات.",
"cLTitle2": "ألبومات ذكية",
"cLTitle3": "معرض محسن",
"cLTitle4": "تمرير أسرع",
"thisWeek": "هذا الأسبوع",
"lastWeek": "الأسبوع الماضي",
"thisMonth": "هذا الشهر",
@@ -1821,4 +1816,4 @@
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1776,14 +1776,6 @@
"same": "Gleich",
"different": "Verschieden",
"sameperson": "Dieselbe Person?",
"cLTitle1": "Erweiterte Bildbearbeitung",
"cLDesc1": "Wir veröffentlichen eine neue und erweiterte Bildbearbeitung, die mehr Bildzuschnitte ermöglicht, vordefinierte Filter für schnelleres Bearbeiten bietet, sowie die Feinabstimmung von Sättigung, Kontrast, Helligkeit und vielem mehr erlaubt. Der neue Editor erlaubt außerdem das Zeichnen auf den Fotos oder das Hinzufügen von Emojis als Sticker.",
"cLTitle2": "Intelligente Alben",
"cLDesc2": "Du kannst jetzt automatisch Fotos von ausgewählten Personen zu jedem Album hinzufügen. Öffne einfach das Album und wähle \"Personen automatisch hinzufügen\" aus dem Menü. Zusammen mit einem geteilten Album kannst Du Fotos mit null Klicks teilen.",
"cLTitle3": "Verbesserte Galerie",
"cLDesc3": "Wir haben die Möglichkeit hinzugefügt, Alben nach Wochen, Monaten und Jahren zu gruppieren. Du kannst jetzt die Galerie mit diesen neuen Gruppierungs-Optionen so anpassen, dass sie genau so aussieht, wie Du möchtest, zusammen mit angepassten Rastern",
"cLTitle4": "Schnelleres Scrollen",
"cLDesc4": "Zusammen mit einem Schwung Änderungen unter der Haube, um das Erlebnis beim Scrollen der Galerie zu verbessern, haben wir außerdem den Scrollbalken mit Markern neu gestaltet, um es Dir zu ermöglichen, schnell in der Zeitleiste zu springen.",
"indexingPausedStatusDescription": "Die Indizierung ist pausiert. Sie wird automatisch fortgesetzt, wenn das Gerät bereit ist. Das Gerät wird als bereit angesehen, wenn sich der Akkustand, die Akkugesundheit und der thermische Zustand in einem gesunden Bereich befinden.",
"thisWeek": "Diese Woche",
"lastWeek": "Letzte Woche",
@@ -1827,5 +1819,117 @@
"type": "int"
}
}
}
},
"videosProcessed": "Videos verarbeitet",
"totalVideos": "Videos insgesamt",
"skippedVideos": "Übersprungene Videos",
"videoStreamingDescriptionLine1": "Videos sofort auf jedem Gerät abspielen.",
"videoStreamingDescriptionLine2": "Aktivieren, um Video-Streams auf diesem Gerät zu verarbeiten.",
"videoStreamingNote": "Nur Videos der letzten 60 Tage und unter einer Minute werden auf diesem Gerät verarbeitet. Für ältere/längere Videos aktiviere das Streaming in der Desktop-App.",
"createStream": "Stream erzeugen",
"recreateStream": "Stream neu erzeugen",
"addedToStreamCreationQueue": "Zur Warteschlange für Streamerstellung hinzugefügt",
"addedToStreamRecreationQueue": "Zur Warteschlange für Neuerstellung der Streams hinzugefügt",
"videoPreviewAlreadyExists": "Videovorschau existiert bereits",
"videoAlreadyInQueue": "Videodatei existiert bereits in der Warteschlange",
"addedToQueue": "Zur Warteschlange hinzugefügt",
"creatingStream": "Stream wird erzeugt",
"similarImages": "Ähnliche Bilder",
"findSimilarImages": "Ähnliche Bilder finden",
"noSimilarImagesFound": "Keine ähnlichen Bilder gefunden",
"yourPhotosLookUnique": "Deine Fotos sehen einzigartig aus",
"similarGroupsFound": "{count, plural, =1{Eine Gruppe gefunden} other{{count} Gruppen gefunden}}",
"@similarGroupsFound": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"reviewAndRemoveSimilarImages": "Überprüfe und lösche ähnliche Bilder",
"deletePhotosWithSize": "Lösche {count} Fotos ({size})",
"@deletePhotosWithSize": {
"placeholders": {
"count": {
"type": "String"
},
"size": {
"type": "String"
}
}
},
"selectionOptions": "Auswahloptionen",
"selectExactWithCount": "Exakt gleich ({count})",
"@selectExactWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectExact": "Exakte auswählen",
"selectSimilarWithCount": "Nahezu gleich ({count})",
"@selectSimilarWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectSimilar": "Ähnliche auswählen",
"selectAllWithCount": "Alle Ähnlichkeiten ({count})",
"@selectAllWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectSimilarImagesTitle": "Ähnliche Bilder auswählen",
"chooseSimilarImagesToSelect": "Wähle Bilder anhand ihrer visuellen Ähnlichkeit",
"clearSelection": "Auswahl aufheben",
"similarImagesCount": "{count} ähnliche Bilder",
"@similarImagesCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"deleteWithCount": "Löschen ({count})",
"@deleteWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"deleteFiles": "Dateien löschen",
"areYouSureDeleteFiles": "Bist du sicher, dass du diese Dateien löschen willst?",
"greatJob": "Gut gemacht!",
"cleanedUpSimilarImages": "Du hast {size} an Speicherplatz freigegeben",
"@cleanedUpSimilarImages": {
"placeholders": {
"size": {
"type": "String"
}
}
},
"size": "Größe",
"similarity": "Ähnlichkeit",
"processingLocally": "Lokale Verarbeitung",
"useMLToFindSimilarImages": "Überprüfe und entferne Bilder, die sich ähnlich sehen.",
"all": "Alle",
"similar": "Ähnlich",
"identical": "Identisch",
"nothingHereTryAnotherFilter": "Nichts zu sehen, probier einen anderen Filter! 👀",
"related": "Verwandt",
"hoorayyyy": "Hurraaaa!",
"nothingToTidyUpHere": "Hier gibt es nichts zu bereinigen",
"cLTitle1": "Ähnliche Bilder",
"cLDesc1": "Wir führen ein neues ML-basiertes System ein, um ähnliche Bilder zu erkennen, mit dem du deine Bibliothek bereinigen kannst. Verfügbar unter Einstellungen->Sicherung->Speicherplatz freigeben",
"cLTitle2": "Manuelle Video-Stream-Generierung",
"cLDesc2": "Du kannst jetzt die Stream-Generierung für Videos direkt aus der App manuell auslösen. Wir haben auch einen neuen Video-Streaming-Einstellungsbildschirm hinzugefügt, der dir zeigt, welcher Prozentsatz deiner Videos für das Streaming verarbeitet wurde",
"cLTitle3": "Leistungsverbesserungen",
"cLDesc3": "Mehrere Verbesserungen im Hintergrund, einschließlich besserer Cache-Nutzung und einer flüssigeren Scroll-Erfahrung"
}

View File

@@ -1776,14 +1776,6 @@
"same": "Same",
"different": "Different",
"sameperson": "Same person?",
"cLTitle1": "Advanced Image Editor",
"cLDesc1": "We are releasing a new and advanced image editor that add more cropping frames, filter presets for quick edits, fine tuning options including saturation, contrast, brightness, temperature and a lot more. The new editor also includes the ability to draw on your photos and add emojis as stickers.",
"cLTitle2": "Smart Albums",
"cLDesc2": "You can now automatically add photos of selected people to any album. Just go the album, and select \"auto-add people\" from the overflow menu. If used along with shared album, you can share photos with zero clicks.",
"cLTitle3": "Improved Gallery",
"cLDesc3": "We have added the ability to group your gallery by weeks, months, and years. You can now customise your gallery to look exactly the way you want with these new grouping options, along with custom grids",
"cLTitle4": "Faster Scroll",
"cLDesc4": "Along with a bunch of under the hood improvements to improve the gallery scroll experience, we have also redesigned the scroll bar to show markers, allowing you to quickly jump across the timeline.",
"indexingPausedStatusDescription": "Indexing is paused. It will automatically resume when the device is ready. The device is considered ready when its battery level, battery health, and thermal status are within a healthy range.",
"thisWeek": "This week",
"lastWeek": "Last week",
@@ -1827,5 +1819,123 @@
"type": "int"
}
}
}
}
},
"videosProcessed": "Videos processed",
"totalVideos": "Total videos",
"skippedVideos": "Skipped videos",
"videoStreamingDescriptionLine1": "Play videos instantly on any device.",
"videoStreamingDescriptionLine2": "Enable to process video streams on this device.",
"videoStreamingNote": "Only videos from last 60 days and under 1 minute are processed on this device. For older/longer videos, enable streaming in the desktop app.",
"createStream": "Create stream",
"recreateStream": "Recreate stream",
"addedToStreamCreationQueue": "Added to stream creation queue",
"addedToStreamRecreationQueue": "Added to stream recreation queue",
"videoPreviewAlreadyExists": "Video preview already exists",
"videoAlreadyInQueue": "Video file already present in the queue",
"addedToQueue": "Added to queue",
"creatingStream": "Creating stream",
"similarImages": "Similar images",
"findSimilarImages": "Find similar images",
"noSimilarImagesFound": "No similar images found",
"yourPhotosLookUnique": "Your photos look unique",
"similarGroupsFound": "{count, plural, =1{{count} group found} other{{count} groups found}}",
"@similarGroupsFound": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"reviewAndRemoveSimilarImages": "Review and remove similar images",
"deletePhotosWithSize": "Delete {count} photos ({size})",
"@deletePhotosWithSize": {
"placeholders": {
"count": {
"type": "String"
},
"size": {
"type": "String"
}
}
},
"selectionOptions": "Selection options",
"selectExactWithCount": "Exactly similar ({count})",
"@selectExactWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectExact": "Select exact",
"selectSimilarWithCount": "Mostly similar ({count})",
"@selectSimilarWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectSimilar": "Select similar",
"selectAllWithCount": "All similarities ({count})",
"@selectAllWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectSimilarImagesTitle": "Select similar images",
"chooseSimilarImagesToSelect": "Select images based on their visual similarity",
"clearSelection": "Clear selection",
"similarImagesCount": "{count} similar images",
"@similarImagesCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"deleteWithCount": "Delete ({count})",
"@deleteWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"deleteFiles": "Delete files",
"areYouSureDeleteFiles": "Are you sure you want to delete these files?",
"greatJob": "Great job!",
"cleanedUpSimilarImages": "You freed up {size} of space",
"@cleanedUpSimilarImages": {
"placeholders": {
"size": {
"type": "String"
}
}
},
"size": "Size",
"similarity": "Similarity",
"analyzingPhotosLocally": "Analyzing your photos locally...",
"lookingForVisualSimilarities": "Looking for visual similarities...",
"comparingImageDetails": "Comparing image details...",
"findingSimilarImages": "Finding similar images...",
"almostDone": "Almost done...",
"processingLocally": "Processing locally",
"useMLToFindSimilarImages": "Review and remove images that look similar to each other.",
"all": "All",
"similar": "Similar",
"identical": "Identical",
"nothingHereTryAnotherFilter": "Nothing here, try another filter! 👀",
"related": "Related",
"hoorayyyy": "Hoorayyyy!",
"nothingToTidyUpHere": "Nothing to tidy up here",
"deletingDash": "Deleting - ",
"cLTitle1": "Similar Images",
"cLDesc1": "We are introducing a new ML-based system to detect similar images, using which you can cleanup your library. Available in Settings->Backup->Free Up Space",
"cLTitle2": "Manual video stream generation",
"cLDesc2": "You can now manually trigger stream generation for videos directly from the app. We have also added a new video streaming settings screen which will show you what percentage of your videos have been processed for streaming",
"cLTitle3": "Performance Improvements",
"cLDesc3": "Multiple under the hood improvements, including better cache usage and a smoother scroll experience"
}

View File

@@ -159,7 +159,7 @@
"addCollaborator": "Agregar colaborador",
"addANewEmail": "Agregar nuevo correo electrónico",
"orPickAnExistingOne": "O elige uno existente",
"collaboratorsCanAddPhotosAndVideosToTheSharedAlbum": "Colaboradores pueden añadir fotos y videos al álbum compartido.",
"collaboratorsCanAddPhotosAndVideosToTheSharedAlbum": "Colaboradores pueden añadir fotos y vídeos al álbum compartido.",
"enterEmail": "Ingresar correo electrónico ",
"albumOwner": "Propietario",
"@albumOwner": {
@@ -270,7 +270,7 @@
"shareTextConfirmOthersVerificationID": "Hola, ¿puedes confirmar que esta es tu ID de verificación ente.io: {verificationID}?",
"somethingWentWrong": "Algo salió mal",
"sendInvite": "Enviar invitación",
"shareTextRecommendUsingEnte": "Descarga Ente para que podamos compartir fácilmente fotos y videos en calidad original.\n\nhttps://ente.io",
"shareTextRecommendUsingEnte": "Descarga Ente para que podamos compartir fácilmente fotos y vídeos en calidad original.\n\nhttps://ente.io",
"done": "Hecho",
"applyCodeTitle": "Usar código",
"enterCodeDescription": "Introduce el código proporcionado por tu amigo para reclamar almacenamiento gratuito para ambos",
@@ -857,7 +857,7 @@
"deviceFilesAutoUploading": "Los archivos añadidos a este álbum de dispositivo se subirán automáticamente a Ente.",
"turnOnBackupForAutoUpload": "Activar la copia de seguridad para subir automáticamente archivos añadidos a la carpeta de este dispositivo a Ente.",
"noHiddenPhotosOrVideos": "No hay fotos ni vídeos ocultos",
"toHideAPhotoOrVideo": "Para ocultar una foto o video",
"toHideAPhotoOrVideo": "Para ocultar una foto o vídeo",
"openTheItem": "• Abrir el elemento",
"clickOnTheOverflowMenu": "• Haga clic en el menú desbordante",
"click": "• Clic",
@@ -866,7 +866,7 @@
"archiveAlbum": "Archivar álbum",
"calculating": "Calculando...",
"pleaseWaitDeletingAlbum": "Por favor espera. Borrando el álbum",
"searchByExamples": "• Nombres de álbumes (por ejemplo, \"Cámara\")\n• Tipos de archivos (por ejemplo, \"Videos\", \".gif\")\n• Años y meses (por ejemplo, \"2022\", \"Enero\")\n• Vacaciones (por ejemplo, \"Navidad\")\n• Descripciones fotográficas (por ejemplo, \"#diversión\")",
"searchByExamples": "• Nombres de álbumes (por ejemplo, \"Cámara\")\n• Tipos de archivos (por ejemplo, \"Vídeos\", \".gif\")\n• Años y meses (por ejemplo, \"2022\", \"Enero\")\n• Vacaciones (por ejemplo, \"Navidad\")\n• Descripciones fotográficas (por ejemplo, \"#diversión\")",
"youCanTrySearchingForADifferentQuery": "Puedes intentar buscar una consulta diferente.",
"noResultsFound": "No se han encontrado resultados",
"addedBy": "Añadido por {emailOrName}",
@@ -884,8 +884,8 @@
"filesSavedToGallery": "Archivo guardado en la galería",
"fileFailedToSaveToGallery": "No se pudo guardar el archivo en la galería",
"download": "Descargar",
"pressAndHoldToPlayVideo": "Presiona y mantén presionado para reproducir el video",
"pressAndHoldToPlayVideoDetailed": "Mantén pulsada la imagen para reproducir el video",
"pressAndHoldToPlayVideo": "Presiona y mantén presionado para reproducir el vídeo",
"pressAndHoldToPlayVideoDetailed": "Mantén pulsada la imagen para reproducir el vídeo",
"downloadFailed": "Descarga fallida",
"deduplicateFiles": "Deduplicar archivos",
"deselectAll": "Deseleccionar todo",
@@ -895,7 +895,7 @@
"count": "Cuenta",
"totalSize": "Tamaño total",
"longpressOnAnItemToViewInFullscreen": "Manten presionado un elemento para ver en pantalla completa",
"decryptingVideo": "Descifrando video...",
"decryptingVideo": "Descifrando vídeo...",
"authToViewYourMemories": "Por favor, autentícate para ver tus recuerdos",
"unlock": "Desbloquear",
"freeUpSpace": "Liberar espacio",
@@ -1014,7 +1014,7 @@
"cachedData": "Datos almacenados en caché",
"clearCaches": "Limpiar cachés",
"remoteImages": "Imágenes remotas",
"remoteVideos": "Videos remotos",
"remoteVideos": "Vídeos remotos",
"remoteThumbnails": "Miniaturas remotas",
"pendingSync": "Sincronización pendiente",
"localGallery": "Galería local",
@@ -1169,7 +1169,7 @@
"description": "Label for the map view"
},
"maps": "Mapas",
"enableMaps": "Activar Mapas",
"enableMaps": "Habilitar mapas",
"enableMapsDesc": "Esto mostrará tus fotos en el mapa mundial.\n\nEste mapa está gestionado por Open Street Map, y la ubicación exacta de tus fotos nunca se comparte.\n\nPuedes deshabilitar esta función en cualquier momento en Ajustes.",
"quickLinks": "Acceso rápido",
"selectItemsToAdd": "Selecciona elementos para agregar",
@@ -1346,7 +1346,7 @@
"noSystemLockFound": "Bloqueo de sistema no encontrado",
"tapToUnlock": "Toca para desbloquear",
"tooManyIncorrectAttempts": "Demasiados intentos incorrectos",
"videoInfo": "Información de video",
"videoInfo": "Información de vídeo",
"autoLock": "Bloqueo automático",
"immediately": "Inmediatamente",
"autoLockFeatureDescription": "Tiempo después de que la aplicación esté en segundo plano",
@@ -1433,7 +1433,7 @@
"description": "In session page, warn user (in toast) that active sessions could not be fetched."
},
"failedToRefreshStripeSubscription": "Error al actualizar la suscripción",
"failedToPlayVideo": "Error al reproducir el video",
"failedToPlayVideo": "Error al reproducir el vídeo",
"uploadIsIgnoredDueToIgnorereason": "La subida se ignoró debido a {ignoreReason}",
"@uploadIsIgnoredDueToIgnorereason": {
"placeholders": {
@@ -1588,7 +1588,7 @@
},
"legacyInvite": "{email} te ha invitado a ser un contacto de confianza",
"authToManageLegacy": "Por favor, autentícate para administrar tus contactos de confianza",
"useDifferentPlayerInfo": "¿Tienes problemas para reproducir este video? Mantén pulsado aquí para probar un reproductor diferente.",
"useDifferentPlayerInfo": "¿Tienes problemas para reproducir este vídeo? Mantén pulsado aquí para probar un reproductor diferente.",
"hideSharedItemsFromHomeGallery": "Ocultar elementos compartidos de la galería de inicio",
"gallery": "Galería",
"joinAlbum": "Unir álbum",
@@ -1662,7 +1662,7 @@
"@linkPersonCaption": {
"description": "Caption for the 'Link person' title. It should be a continuation of the 'Link person' title. Just like how 'Link person' + 'for better sharing experience' forms a proper sentence in English, the combination of these two strings should also be a proper sentence in other languages."
},
"videoStreaming": "Vídeos en streaming",
"videoStreaming": "Vídeos en transmisión",
"processingVideos": "Procesando vídeos",
"streamDetails": "Detalles de la transmisión",
"processing": "Procesando",
@@ -1776,5 +1776,160 @@
"same": "Igual",
"different": "Diferente",
"sameperson": "la misma persona?",
"indexingPausedStatusDescription": "La indexación está pausada. Se reanudará automáticamente cuando el dispositivo esté listo. El dispositivo se considera listo cuando su nivel de batería, la salud de la batería y temperatura están en un rango saludable."
"indexingPausedStatusDescription": "La indexación está pausada. Se reanudará automáticamente cuando el dispositivo esté listo. El dispositivo se considera listo cuando su nivel de batería, la salud de la batería y temperatura están en un rango saludable.",
"thisWeek": "Esta semana",
"lastWeek": "Semana pasada",
"thisMonth": "Este mes",
"thisYear": "Este año",
"groupBy": "Agrupar por",
"faceThumbnailGenerationFailed": "No se pueden generar las miniaturas de cara",
"fileAnalysisFailed": "No se puede analizar el archivo",
"editAutoAddPeople": "Editar agregar personas automáticamente",
"autoAddPeople": "Agregar personas automáticamente",
"autoAddToAlbum": "Añadir al álbum automáticamente",
"shouldRemoveFilesSmartAlbumsDesc": "¿Deben eliminarse los archivos relacionados con la persona previamente seleccionada en los álbumes inteligentes?",
"addingPhotos": "Añadiendo fotos",
"gettingReady": "Preparándose",
"addSomePhotosDesc1": "Añadir algunas fotos o elegir ",
"addSomePhotosDesc2": "caras familiares",
"addSomePhotosDesc3": "\npara comenzar con",
"ignorePerson": "Ignorar persona",
"mixedGrouping": "¿Grupo mixto?",
"analysis": "Análisis",
"doesGroupContainMultiplePeople": "¿Esta agrupación contiene varias personas?",
"automaticallyAnalyzeAndSplitGrouping": "Analizaremos automáticamente la agrupación para determinar si hay varias personas presentes, y separarlas de nuevo. Esto puede tardar unos segundos.",
"layout": "Disposición",
"day": "Día",
"peopleAutoAddDesc": "Selecciona las personas que quieres añadir automáticamente al álbum",
"undo": "Deshacer",
"redo": "Rehacer",
"filter": "Filtro",
"adjust": "Ajustar",
"draw": "Dibujar",
"sticker": "Pegatina",
"brushColor": "Color del pincel",
"font": "Fuente",
"background": "Fondo",
"align": "Alinear",
"addedToAlbums": "{count, plural, one {}=1{Añadido con éxito a 1 álbum} other{Añadido con éxito a {count} álbumes}}",
"@addedToAlbums": {
"description": "Message shown when items are added to albums",
"placeholders": {
"count": {
"type": "int"
}
}
},
"videosProcessed": "Vídeos procesados",
"totalVideos": "Vídeos totales",
"skippedVideos": "Vídeos omitidos",
"videoStreamingDescriptionLine1": "Reproduce vídeos al instante en cualquier dispositivo.",
"videoStreamingDescriptionLine2": "Habilitar para procesar transmisiones de vídeo en este dispositivo.",
"videoStreamingNote": "Solo los vídeos de los últimos 60 días y menos de 1 minuto se procesan en este dispositivo. Para vídeos más viejos/más largos, habilita la transmisión en la aplicación de escritorio.",
"createStream": "Crear transmisión",
"recreateStream": "Recrear transmisión",
"addedToStreamCreationQueue": "Añadido a la cola de creación de transmisiones",
"addedToStreamRecreationQueue": "Añadido a la cola de grabación de transmisiones",
"videoPreviewAlreadyExists": "La vista previa de vídeo ya existe",
"videoAlreadyInQueue": "El archivo de vídeo ya está en la cola",
"addedToQueue": "Añadido a la cola",
"creatingStream": "Creando transmisión",
"similarImages": "Imágenes similares",
"findSimilarImages": "Buscar imágenes similares",
"noSimilarImagesFound": "No se encontraron imágenes similares",
"yourPhotosLookUnique": "Tus fotos se ven únicas",
"similarGroupsFound": "{count, plural, one {}=1{{count} grupo encontrado} other{{count} grupos encontrados}}",
"@similarGroupsFound": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"reviewAndRemoveSimilarImages": "Revisar y eliminar imágenes similares",
"deletePhotosWithSize": "Eliminar {count} fotos ({size})",
"@deletePhotosWithSize": {
"placeholders": {
"count": {
"type": "String"
},
"size": {
"type": "String"
}
}
},
"selectionOptions": "Opciones de selección",
"selectExactWithCount": "Exactamente similar ({count})",
"@selectExactWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectExact": "Seleccionar exactos",
"selectSimilarWithCount": "Mayormente, similar ({count})",
"@selectSimilarWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectSimilar": "Seleccionar similares",
"selectAllWithCount": "Todas las similitudes ({count})",
"@selectAllWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectSimilarImagesTitle": "Seleccionar imágenes similares",
"chooseSimilarImagesToSelect": "Seleccionar imágenes basándose en su similitud visual",
"clearSelection": "Borrar selección",
"similarImagesCount": "{count} imágenes similares",
"@similarImagesCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"deleteWithCount": "Eliminar ({count})",
"@deleteWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"deleteFiles": "Eliminar archivos",
"areYouSureDeleteFiles": "¿Estás seguro que quieres eliminar estos archivos?",
"greatJob": "¡Bien hecho!",
"cleanedUpSimilarImages": "Has liberado {size} de espacio",
"@cleanedUpSimilarImages": {
"placeholders": {
"size": {
"type": "String"
}
}
},
"size": "Tamaño",
"similarity": "Similitud",
"processingLocally": "Procesando localmente",
"useMLToFindSimilarImages": "Revisar y eliminar imágenes que se parecen entre sí.",
"all": "Todas",
"similar": "Similares",
"identical": "Idénticas",
"nothingHereTryAnotherFilter": "Nada aquí, ¡prueba con otro filtro! 👀",
"related": "Relacionado",
"hoorayyyy": "¡Hurraaaa!",
"nothingToTidyUpHere": "Nada que limpiar aquí",
"cLTitle1": "Imágenes Similares",
"cLDesc1": "Estamos introduciendo un nuevo sistema basado en ML para detectar imágenes similares, con el cual puedes limpiar tu biblioteca. Disponible en Configuración->Copia de Seguridad->Liberar Espacio",
"cLTitle2": "Generación manual de transmisión de video",
"cLDesc2": "Ahora puedes activar manualmente la generación de transmisión para videos directamente desde la aplicación. También hemos agregado una nueva pantalla de configuración de transmisión de video que te mostrará qué porcentaje de tus videos han sido procesados para transmisión",
"cLTitle3": "Mejoras de Rendimiento",
"cLDesc3": "Múltiples mejoras internas, incluyendo mejor uso de caché y una experiencia de desplazamiento más fluida"
}

View File

@@ -1776,14 +1776,6 @@
"same": "Identique",
"different": "Différent(e)",
"sameperson": "Même personne ?",
"cLTitle1": "Éditeur d'image avancé",
"cLDesc1": "Nous déployons un nouvel éditeur d'image avancé qui ajoute plus d'options de rognage, des filtres, des préréglages pour des modifications rapides ainsi que des options de réglage fin (la saturation, le contraste, la luminosité, la température et beaucoup plus). Le nouvel éditeur inclut également la possibilité de dessiner sur vos photos et d'ajouter des emojis en tant qu'autocollants.",
"cLTitle2": "Albums Intelligents",
"cLDesc2": "Vous pouvez maintenant ajouter automatiquement des photos de personnes sélectionnées à n'importe quel album. Allez simplement à l'album et sélectionnez \"Ajouter automatiquement des personnes\" dans le menu déroulant. Couplé avec un album partagé, vous pouvez partager des photos en zéro clic.",
"cLTitle3": "Galerie améliorée",
"cLDesc3": "Nous avons ajouté la possibilité de regrouper votre galerie par semaines, mois et années. Vous pouvez maintenant personnaliser votre galerie pour qu'elle soit exactement comme vous le souhaitez avec ces nouvelles options de regroupement, ainsi que des grilles personnalisées",
"cLTitle4": "Défilement plus rapide",
"cLDesc4": "En plus des quelques améliorations pour améliorer l'expérience de défilement de la galerie, nous avons également redessiné la barre de défilement pour afficher des marqueurs, ce qui vous permet de sauter rapidement dans la chronologie.",
"indexingPausedStatusDescription": "L'indexation est en pause. Elle reprendra automatiquement lorsque l'appareil sera prêt. Celui-ci est considéré comme prêt lorsque le niveau de batterie, sa santé et son état thermique sont dans une plage saine.",
"thisWeek": "Cette semaine",
"lastWeek": "La semaine dernière",
@@ -1827,5 +1819,109 @@
"type": "int"
}
}
}
},
"videosProcessed": "Vidéos traitées",
"totalVideos": "Total de vidéos",
"skippedVideos": "Vidéos ignorées",
"videoStreamingDescriptionLine1": "Lire instantanément des vidéos sur n'importe quel appareil.",
"videoStreamingDescriptionLine2": "Activer pour traiter les flux vidéo sur cet appareil.",
"videoStreamingNote": "Seules les vidéos des 60 derniers jours et de moins d'une minute sont traitées sur cet appareil. Pour les vidéos plus anciennes/plus longues, activez le streaming dans l'application pour ordinateur.",
"createStream": "Créer le flux",
"recreateStream": "Recréer le flux",
"addedToStreamCreationQueue": "Ajouté à la file d'attente de création de flux",
"addedToStreamRecreationQueue": "Ajouté à la file d'attente de re-création de flux",
"videoPreviewAlreadyExists": "L'aperçu vidéo existe déjà",
"videoAlreadyInQueue": "Fichier vidéo déjà présent dans la file d'attente",
"addedToQueue": "Ajouté à la file d'attente",
"creatingStream": "Création du flux",
"similarImages": "Images similaires",
"findSimilarImages": "Rechercher des images similaires",
"noSimilarImagesFound": "Aucune image similaire trouvée",
"yourPhotosLookUnique": "Vos photos semblent uniques",
"reviewAndRemoveSimilarImages": "Examiner et supprimer les images similaires",
"deletePhotosWithSize": "Supprimer {count} photos ({size})",
"@deletePhotosWithSize": {
"placeholders": {
"count": {
"type": "String"
},
"size": {
"type": "String"
}
}
},
"selectionOptions": "Options de sélection",
"selectExactWithCount": "Exactement similaire ({count})",
"@selectExactWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectExact": "Sélectionner exactement",
"selectSimilarWithCount": "Plutôt similaire ({count})",
"@selectSimilarWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectSimilar": "Sélectionner à l'identique",
"selectAllWithCount": "Toutes les similarités ({count})",
"@selectAllWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectSimilarImagesTitle": "Sélectionner les images similaires",
"chooseSimilarImagesToSelect": "Sélectionnez des images en fonction de leur similitude visuelle",
"clearSelection": "Effacer la sélection",
"similarImagesCount": "{count} images similaires",
"@similarImagesCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"deleteWithCount": "Supprimer ({count})",
"@deleteWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"deleteFiles": "Supprimer les fichiers",
"areYouSureDeleteFiles": "Êtes-vous sûr de vouloir supprimer ces fichiers ?",
"greatJob": "Excellent !",
"cleanedUpSimilarImages": "Vous avez libéré {size} d'espace",
"@cleanedUpSimilarImages": {
"placeholders": {
"size": {
"type": "String"
}
}
},
"size": "Taille",
"similarity": "Similitude",
"processingLocally": "Traitement local",
"useMLToFindSimilarImages": "Examinez et supprimez les images qui se ressemblent entre elles.",
"all": "Toutes",
"similar": "Similaires",
"identical": "Identiques",
"nothingHereTryAnotherFilter": "Rien ici, essayez un autre filtre ! 👀",
"related": "Liés",
"hoorayyyy": "Houraaa !",
"nothingToTidyUpHere": "Rien à nettoyer ici",
"cLTitle1": "Images Similaires",
"cLDesc1": "Nous introduisons un nouveau système basé sur l'IA pour détecter les images similaires, avec lequel vous pouvez nettoyer votre bibliothèque. Disponible dans Paramètres->Sauvegarde->Libérer de l'espace",
"cLTitle2": "Génération manuelle de flux vidéo",
"cLDesc2": "Vous pouvez maintenant déclencher manuellement la génération de flux pour les vidéos directement depuis l'application. Nous avons également ajouté un nouvel écran de paramètres de streaming vidéo qui vous montrera quel pourcentage de vos vidéos ont été traitées pour le streaming",
"cLTitle3": "Améliorations de performance",
"cLDesc3": "Plusieurs améliorations internes, incluant une meilleure utilisation du cache et une expérience de défilement plus fluide"
}

View File

@@ -189,6 +189,7 @@
"allowAddPhotosDescription": "Izinkan orang yang memiliki link untuk menambahkan foto ke album berbagi ini.",
"passwordLock": "Kunci dengan sandi",
"canNotOpenTitle": "Tidak dapat membuka album ini",
"canNotOpenBody": "Maaf, album ini tidak dapat dibuka di aplikasi.",
"disableDownloadWarningTitle": "Perlu diketahui",
"disableDownloadWarningBody": "Orang yang melihat masih bisa mengambil tangkapan layar atau menyalin foto kamu menggunakan alat eksternal",
"allowDownloads": "Izinkan pengunduhan",
@@ -355,6 +356,7 @@
"failedToLoadAlbums": "Gagal memuat album",
"hidden": "Tersembunyi",
"authToViewYourHiddenFiles": "Harap autentikasi untuk melihat file tersembunyi kamu",
"authToViewTrashedFiles": "Silakan autentikasi untuk melihat file anda yang ada di tong sampah",
"trash": "Sampah",
"uncategorized": "Tak Berkategori",
"videoSmallCase": "video",
@@ -370,6 +372,21 @@
"deleteFromBoth": "Hapus dari keduanya",
"newAlbum": "Album baru",
"albums": "Album",
"memoryCount": "{count, plural, =0{tidak ada memori} one{{formattedCount} memori} other{{formattedCount} memori}}",
"@memoryCount": {
"description": "The text to display the number of memories",
"type": "text",
"placeholders": {
"count": {
"example": "1",
"type": "int"
},
"formattedCount": {
"type": "String",
"example": "11.513, 11,511"
}
}
},
"selectedPhotos": "{count} terpilih",
"@selectedPhotos": {
"description": "Display the number of selected photos",
@@ -419,6 +436,7 @@
"discover_receipts": "Tanda Terima",
"discover_notes": "Catatan",
"discover_memes": "Meme",
"discover_visiting_cards": "Kartu Nama",
"discover_babies": "Bayi",
"discover_pets": "Hewan",
"discover_selfies": "Swafoto",
@@ -427,6 +445,7 @@
"discover_celebrations": "Perayaan",
"discover_sunset": "Senja",
"discover_hills": "Bukit",
"discover_greenery": "Hijau-hijauan",
"mlIndexingDescription": "Perlu diperhatikan bahwa pemelajaran mesin dapat meningkatkan penggunaan data dan baterai perangkat hingga seluruh item selesai terindeks. Gunakan aplikasi desktop untuk pengindeksan lebih cepat, seluruh hasil akan tersinkronkan secara otomatis.",
"loadingModel": "Mengunduh model...",
"waitingForWifi": "Menunggu WiFi...",
@@ -442,6 +461,21 @@
"updatingFolderSelection": "Memperbaharui pilihan folder...",
"itemCount": "{count, plural, other{{count} item}}",
"deleteItemCount": "{count, plural, =1 {Hapus {count} item} other {Hapus {count} item}}",
"duplicateItemsGroup": "{count} berkas, masing-masing {formattedSize}",
"@duplicateItemsGroup": {
"description": "Display the number of duplicate files and their size",
"type": "text",
"placeholders": {
"count": {
"example": "12",
"type": "int"
},
"formattedSize": {
"example": "2.3 MB",
"type": "String"
}
}
},
"showMemories": "Lihat kenangan",
"yearsAgo": "{count, plural, other{{count} tahun lalu}}",
"backupSettings": "Pengaturan pencadangan",
@@ -492,6 +526,7 @@
"viewLargeFiles": "File berukuran besar",
"viewLargeFilesDesc": "Tampilkan file yang paling besar mengonsumsi ruang penyimpanan.",
"noDuplicates": "✨ Tak ada file duplikat",
"youveNoDuplicateFilesThatCanBeCleared": "Anda tidak memiliki file duplikat yang dapat dihapus",
"success": "Berhasil",
"rateUs": "Beri kami nilai",
"remindToEmptyDeviceTrash": "Kosongkan juga “Baru Dihapus” dari “Pengaturan” -> “Penyimpanan” untuk memperoleh ruang yang baru saja dibersihkan",
@@ -658,6 +693,7 @@
"rateTheApp": "Nilai app ini",
"startBackup": "Mulai pencadangan",
"noPhotosAreBeingBackedUpRightNow": "Tidak ada foto yang sedang dicadangkan sekarang",
"preserveMore": "Amankan lebih banyak",
"grantFullAccessPrompt": "Harap berikan akses ke semua foto di app Pengaturan",
"allowPermTitle": "Izinkan akses ke foto",
"allowPermBody": "Ijinkan akses ke foto Anda dari Pengaturan agar Ente dapat menampilkan dan mencadangkan pustaka Anda.",
@@ -714,6 +750,7 @@
"lastUpdated": "Terakhir diperbarui",
"deleteEmptyAlbums": "Hapus album kosong",
"deleteEmptyAlbumsWithQuestionMark": "Hapus album yang kosong?",
"deleteAlbumsDialogBody": "Ini akan menghapus semua album kosong. Ini berguna ketika anda ingin mengurangi kekacauan di daftar album anda.",
"deleteProgress": "Menghapus {currentlyDeleting} / {totalCount}",
"genericProgress": "Memproses {currentlyProcessing} / {totalCount}",
"@genericProgress": {
@@ -731,6 +768,7 @@
}
},
"permanentlyDelete": "Hapus secara permanen",
"canOnlyCreateLinkForFilesOwnedByYou": "Hanya dapat membuat tautan untuk file yang dimiliki oleh anda",
"publicLinkCreated": "Link publik dibuat",
"youCanManageYourLinksInTheShareTab": "Kamu bisa atur link yang telah kamu buat di tab berbagi.",
"linkCopiedToClipboard": "Link tersalin ke papan klip",
@@ -740,15 +778,30 @@
"type": "text"
},
"moveToAlbum": "Pindahkan ke album",
"unhide": "Tampilkan",
"unarchive": "Keluarkan dari arsip",
"favorite": "Favorit",
"removeFromFavorite": "Hapus dari favorit",
"shareLink": "Bagikan link",
"createCollage": "Buat kolase",
"saveCollage": "Simpan kolase",
"collageSaved": "Kolase tersimpan ke galeri",
"collageLayout": "Tata letak",
"addToEnte": "Tambah ke Ente",
"addToAlbum": "Tambah ke album",
"delete": "Hapus",
"hide": "Sembunyikan",
"share": "Bagikan",
"unhideToAlbum": "Tampikan ke album",
"restoreToAlbum": "Pulihkan ke album",
"moveItem": "{count, plural, =1 {Pindahkan item} other {Pindahkan item}}",
"@moveItem": {
"description": "Page title while moving one or more items to an album"
},
"addItem": "{count, plural, =1 {Tambahkan item} other {Tambahkan item}}",
"@addItem": {
"description": "Page title while adding one or more items to album"
},
"createOrSelectAlbum": "Buat atau pilih album",
"selectAlbum": "Pilih album",
"searchByAlbumNameHint": "Nama album",
@@ -756,18 +809,33 @@
"enterAlbumName": "Masukkan nama album",
"restoringFiles": "Memulihkan file...",
"movingFilesToAlbum": "Memindahkan file ke album...",
"unhidingFilesToAlbum": "Tampilkan berkas ke album",
"canNotUploadToAlbumsOwnedByOthers": "Tidak dapat mengunggah album yang dimiliki oleh orang lain",
"uploadingFilesToAlbum": "Mengunggah file ke album...",
"addedSuccessfullyTo": "Berhasil ditambahkan ke {albumName}",
"movedSuccessfullyTo": "Berhasil dipindahkan ke {albumName}",
"thisAlbumAlreadyHDACollaborativeLink": "Link kolaborasi untuk album ini sudah terbuat",
"collaborativeLinkCreatedFor": "Link kolaborasi terbuat untuk {albumName}",
"askYourLovedOnesToShare": "Minta orang terkasih anda untuk berbagi",
"invite": "Undang",
"shareYourFirstAlbum": "Bagikan album pertamamu",
"sharedWith": "Dibagikan dengan {emailIDs}",
"sharedWithMe": "Dibagikan dengan saya",
"sharedByMe": "Dibagikan oleh saya",
"doubleYourStorage": "Gandakan kuota kamu",
"referFriendsAnd2xYourPlan": "Ajak teman dan gandakan paket anda",
"shareAlbumHint": "Buka album lalu ketuk tombol bagikan di sudut kanan atas untuk berbagi.",
"itemsShowTheNumberOfDaysRemainingBeforePermanentDeletion": "Item menampilkan jumlah hari yang tersisa sebelum dihapus permanen",
"trashDaysLeft": "{count, plural, =0 {Segera} =1 {1 hari} other {{count} hari}}",
"@trashDaysLeft": {
"description": "Text to indicate number of days remaining before permanent deletion",
"placeholders": {
"count": {
"example": "1|2|3",
"type": "int"
}
}
},
"deleteAll": "Hapus Semua",
"renameAlbum": "Ubah nama album",
"convertToAlbum": "Ubah menjadi album",
@@ -782,13 +850,16 @@
"leaveSharedAlbum": "Tinggalkan album bersama?",
"leaveAlbum": "Tinggalkan album",
"photosAddedByYouWillBeRemovedFromTheAlbum": "Foto yang telah kamu tambahkan akan dihapus dari album ini",
"youveNoFilesInThisAlbumThatCanBeDeleted": "Anda tidak memiliki file di album ini yang dapat dihapus",
"youDontHaveAnyArchivedItems": "Kamu tidak memiliki item di arsip.",
"ignoredFolderUploadReason": "Sejumlah file di album ini tidak terunggah karena telah dihapus sebelumnya dari Ente.",
"resetIgnoredFiles": "Atur ulang file yang diabaikan",
"deviceFilesAutoUploading": "File yang ditambahkan ke album perangkat ini akan diunggah ke Ente secara otomatis.",
"turnOnBackupForAutoUpload": "Aktifkan pencadangan untuk mengunggah file yang ditambahkan ke folder ini ke Ente secara otomatis.",
"noHiddenPhotosOrVideos": "Tidak ada foto atau video tersembunyi",
"toHideAPhotoOrVideo": "Untuk menyembunyikan foto atau video",
"openTheItem": "• Buka item-nya",
"clickOnTheOverflowMenu": "• Klik pada menu overflow",
"click": "• Click",
"nothingToSeeHere": "Tidak ada apa-apa di sini! 👀",
"unarchiveAlbum": "Keluarkan album dari arsip",
@@ -796,6 +867,7 @@
"calculating": "Menghitung...",
"pleaseWaitDeletingAlbum": "Harap tunggu, sedang menghapus album",
"searchByExamples": "• Nama album (cth. \"Kamera\")\n• Jenis file (cth. \"Video\", \".gif\")\n• Tahun atau bulan (cth. \"2022\", \"Januari\")\n• Musim liburan (cth. \"Natal\")\n• Keterangan foto (cth. “#seru”)",
"youCanTrySearchingForADifferentQuery": "Anda dapat mencoba mencari dengan kata-kata yang berbeda",
"noResultsFound": "Tidak ditemukan hasil",
"addedBy": "Ditambahkan oleh {emailOrName}",
"loadingExifData": "Memuat data EXIF...",
@@ -804,6 +876,7 @@
"thisImageHasNoExifData": "Gambar ini tidak memiliki data exif",
"exif": "EXIF",
"noResults": "Tidak ada hasil",
"weDontSupportEditingPhotosAndAlbumsThatYouDont": "Kami belum mendukung pengeditan foto dan album yang bukan milik anda",
"failedToFetchOriginalForEdit": "Gagal memuat file asli untuk mengedit",
"close": "Tutup",
"setAs": "Pasang sebagai",
@@ -814,12 +887,19 @@
"pressAndHoldToPlayVideo": "Tekan dan tahan untuk memutar video",
"pressAndHoldToPlayVideoDetailed": "Tekan dan tahan gambar untuk memutar video",
"downloadFailed": "Gagal mengunduh",
"deduplicateFiles": "Hilangkan Duplikasi File",
"deselectAll": "Batalkan semua pilihan",
"reviewDeduplicateItems": "Silakan lihat dan hapus item yang merupakan duplikat.",
"clubByCaptureTime": "Kelompokkan berdasarkan waktu pengambilan",
"clubByFileName": "Kelompokkan berdasarkan nama berkas",
"count": "Jumlah",
"totalSize": "Ukuran total",
"longpressOnAnItemToViewInFullscreen": "Tekan lama pada item untuk melihat dalam layar penuh",
"decryptingVideo": "Mendekripsi video...",
"authToViewYourMemories": "Harap autentikasi untuk melihat kenanganmu",
"unlock": "Buka",
"freeUpSpace": "Bersihkan ruang",
"freeUpSpaceSaving": "{count, plural, =1 {Itu dapat dihapus dari perangkat untuk mengosongkan {formattedSize}} other {Itu dapat dihapus dari perangkat untuk mengosongkan {formattedSize}}}",
"filesBackedUpInAlbum": "{count, plural, other {{formattedNumber} file}} dalam album ini telah berhasil dicadangkan",
"@filesBackedUpInAlbum": {
"description": "Text to tell user how many files have been backed up in the album",
@@ -850,11 +930,24 @@
}
}
},
"@freeUpSpaceSaving": {
"description": "Text to tell user how much space they can free up by deleting items from the device"
},
"freeUpAccessPostDelete": "anda masih dapat mengakses {count, plural, =1 {itu} other {mereka}} di Ente selama anda memiliki langganan aktif",
"@freeUpAccessPostDelete": {
"placeholders": {
"count": {
"example": "1",
"type": "int"
}
}
},
"freeUpAmount": "Bersihkan {sizeInMBorGB}",
"thisEmailIsAlreadyInUse": "Email ini telah digunakan",
"incorrectCode": "Kode salah",
"authenticationFailedPleaseTryAgain": "Autentikasi gagal, silakan coba lagi",
"verificationFailedPleaseTryAgain": "Verifikasi gagal, silakan coba lagi",
"authenticating": "Autentikasi...",
"authenticationSuccessful": "Autentikasi berhasil!",
"incorrectRecoveryKey": "Kunci pemulihan salah",
"theRecoveryKeyYouEnteredIsIncorrect": "Kunci pemulihan yang kamu masukkan salah",
@@ -865,12 +958,35 @@
"sorryTheCodeYouveEnteredIsIncorrect": "Maaf, kode yang kamu masukkan salah",
"yourVerificationCodeHasExpired": "Kode verifikasi kamu telah kedaluwarsa",
"emailChangedTo": "Email diubah menjadi {newEmail}",
"verifying": "Memverifikasi...",
"disablingTwofactorAuthentication": "Menonaktifkan autentikasi dua langkah...",
"allMemoriesPreserved": "Semua kenangan terpelihara",
"loadingGallery": "Memuat galeri...",
"syncing": "Menyinkronkan...",
"encryptingBackup": "Mengenkripsi cadangan...",
"syncStopped": "Sinkronisasi terhenti",
"syncProgress": "{completed}/{total} memori tersimpan",
"uploadingMultipleMemories": "Menyimpan {count} memori...",
"@uploadingMultipleMemories": {
"description": "Text to tell user how many memories are being preserved",
"placeholders": {
"count": {
"type": "String"
}
}
},
"uploadingSingleMemory": "Menyimpan 1 memori...",
"@syncProgress": {
"description": "Text to tell user how many memories have been preserved",
"placeholders": {
"completed": {
"type": "String"
},
"total": {
"type": "String"
}
}
},
"archiving": "Mengarsipkan...",
"unarchiving": "Mengeluarkan dari arsip...",
"successfullyArchived": "Berhasil diarsipkan",
@@ -885,6 +1001,8 @@
"empty": "Kosongkan",
"couldNotFreeUpSpace": "Tidak dapat membersihkan ruang",
"permanentlyDeleteFromDevice": "Hapus dari perangkat secara permanen?",
"someOfTheFilesYouAreTryingToDeleteAre": "Beberapa file yang anda coba hapus hanya tersedia di perangkat anda dan tidak dapat dipulihkan jika dihapus",
"theyWillBeDeletedFromAllAlbums": "Mereka akan dihapus dari semua album.",
"someItemsAreInBothEnteAndYourDevice": "Sejumlah item tersimpan di Ente serta di perangkat ini.",
"selectedItemsWillBeDeletedFromAllAlbumsAndMoved": "Item terpilih akan dihapus dari semua album dan dipindahkan ke sampah.",
"theseItemsWillBeDeletedFromYourDevice": "Item ini akan dihapus dari perangkat ini.",
@@ -894,12 +1012,17 @@
"networkHostLookUpErr": "Tidak dapat terhubung dengan Ente, harap periksa pengaturan jaringan kamu dan hubungi dukungan jika masalah berlanjut.",
"networkConnectionRefusedErr": "Tidak dapat terhubung dengan Ente, silakan coba lagi setelah beberapa saat. Jika masalah berlanjut, harap hubungi dukungan.",
"cachedData": "Data cache",
"clearCaches": "Bersihkan cache",
"remoteImages": "Gambar jarak jauh",
"remoteVideos": "Video jarak jauh",
"remoteThumbnails": "Thumbnail jarak jauh",
"pendingSync": "Sinkronisasi tertunda",
"localGallery": "Galeri lokal",
"todaysLogs": "Log hari ini",
"viewLogs": "Lihat log",
"logsDialogBody": "Ini akan mengirimkan log untuk membantu kami memperbaiki masalah anda. Harap diperhatikan bahwa nama file akan disertakan untuk membantu melacak masalah pada file tertentu.",
"preparingLogs": "Menyiapkan log...",
"emailYourLogs": "Kirim log anda melalui email",
"pleaseSendTheLogsTo": "Silakan kirim log-nya ke \n{toEmail}",
"copyEmailAddress": "Salin alamat email",
"exportLogs": "Ekspor log",

View File

@@ -1745,5 +1745,11 @@
"birthdayNotifications": "Notifiche dei compleanni",
"receiveRemindersOnBirthdays": "Ricevi promemoria quando è il compleanno di qualcuno. Toccare la notifica ti porterà alle foto della persona che compie gli anni.",
"happyBirthday": "Buon compleanno! 🥳",
"birthdays": "Compleanni"
"birthdays": "Compleanni",
"cLTitle1": "Immagini Simili",
"cLDesc1": "Stiamo introducendo un nuovo sistema basato su ML per rilevare immagini simili, con il quale puoi pulire la tua libreria. Disponibile in Impostazioni->Backup->Libera Spazio",
"cLTitle2": "Generazione manuale stream video",
"cLDesc2": "Ora puoi attivare manualmente la generazione di stream per i video direttamente dall'app. Abbiamo anche aggiunto una nuova schermata delle impostazioni di streaming video che ti mostrerà quale percentuale dei tuoi video è stata elaborata per lo streaming",
"cLTitle3": "Miglioramenti delle Prestazioni",
"cLDesc3": "Multipli miglioramenti interni, incluso un miglior utilizzo della cache e un'esperienza di scorrimento più fluida"
}

View File

@@ -1665,5 +1665,11 @@
"moon": "月明かりの中",
"onTheRoad": "再び道で",
"food": "料理を楽しむ",
"pets": "毛むくじゃらな仲間たち"
"pets": "毛むくじゃらな仲間たち",
"cLTitle1": "類似画像",
"cLDesc1": "類似画像を検出する新しいML基盤システムを導入し、ライブラリをクリーンアップできます。設定->バックアップ->容量を空けるで利用可能",
"cLTitle2": "手動ビデオストリーム生成",
"cLDesc2": "アプリから直接、ビデオのストリーム生成を手動でトリガーできるようになりました。また、ビデオのうち何パーセントがストリーミング用に処理されたかを表示する新しいビデオストリーミング設定画面も追加しました",
"cLTitle3": "パフォーマンスの改善",
"cLDesc3": "より良いキャッシュ使用とよりスムーズなスクロール体験を含む、複数の内部改善"
}

View File

@@ -1776,14 +1776,6 @@
"same": "Tas pats",
"different": "Skirtingas",
"sameperson": "Tas pats asmuo?",
"cLTitle1": "Pažangi vaizdų rengyklė",
"cLDesc1": "Mes išleidžiame naują ir pažangią vaizdų rengyklę, kurioje yra daugiau apkirpimo rėmelių, filtro nustatymų sparčiams redagavimams, tikslaus sureguliavimo parinkčių, įskaitant sodrumą, kontrastą, skaistį, temperatūrą ir daug daugiau. Naujoji rengyklė taip pat suteikia galimybę piešti ant nuotraukų ir pridėti jaustukus kaip lipdukus.",
"cLTitle2": "Išmanieji albumai",
"cLDesc2": "Dabar galite automatiškai įtraukti pasirinktų asmenų nuotraukas į bet kurį albumą. Tiesiog eikite į albumą ir iš išskleidžiamojo meniu pasirinkite „Automatiškai įtraukti asmenis“. Jei naudojama kartu su bendrinimu albumu, nuotraukas galite bendrinti be jokių paspaudimų.",
"cLTitle3": "Patobulinta galerija",
"cLDesc3": "Pridėjome galimybę sugrupuoti galeriją pagal savaites, mėnesius ir metus. Dabar galite pritaikyti galeriją taip, kad ji atrodytų būtent taip, kaip norite su šiomis naujomis grupavimo parinktimis ir pasirinktiniais tinkleliais.",
"cLTitle4": "Spartesnis slinkimas",
"cLDesc4": "Kartu su daugybe vidinių patobulinimų pagerinti galerijos slinkimo patirtį, mes taip pat pertvarkėme slinkties juostą, kad joje būtų rodomi žymekliai, leidžiantys sparčiai pereiti per laiko juostą.",
"indexingPausedStatusDescription": "Indeksavimas pristabdytas. Jis bus automatiškai tęsiamas, kai įrenginys bus parengtas. Įrenginys laikomas parengtu, kai jo akumuliatoriaus įkrovos lygis, akumuliatoriaus būklė ir terminė būklė yra normos ribose.",
"thisWeek": "Šią savaitę",
"lastWeek": "Praėjusią savaitę",
@@ -1818,5 +1810,16 @@
"brushColor": "Teptuko spalva",
"font": "Šriftas",
"background": "Fonas",
"align": "Lygiuoti"
}
"align": "Lygiuoti",
"similarImages": "Panašūs vaizdai",
"findSimilarImages": "Rasti panašų vaizdų",
"noSimilarImagesFound": "Panašių vaizdų nerasta",
"yourPhotosLookUnique": "Jūsų nuotraukos atrodo ypatingos",
"selectionOptions": "Pasirinkimo parinktys",
"deleteFiles": "Ištrinti failus",
"areYouSureDeleteFiles": "Ar tikrai norite ištrinti šiuos failus?",
"greatJob": "Puiku!",
"size": "Dydis",
"similarity": "Panašumas",
"processingLocally": "Apdorojama vietoje"
}

View File

@@ -1772,5 +1772,11 @@
"thePersonWillNotBeDisplayed": "De persoon wordt niet meer getoond in de personen sectie. Foto's blijven ongemoeid.",
"areYouSureYouWantToMergeThem": "Weet je zeker dat je ze wilt samenvoegen?",
"allUnnamedGroupsWillBeMergedIntoTheSelectedPerson": "Alle naamloze groepen worden samengevoegd met de geselecteerde persoon. Dit kan nog steeds ongedaan worden gemaakt vanuit het geschiedenisoverzicht van de persoon.",
"yesIgnore": "Ja, negeer"
"yesIgnore": "Ja, negeer",
"cLTitle1": "Vergelijkbare Afbeeldingen",
"cLDesc1": "We introduceren een nieuw ML-gebaseerd systeem om vergelijkbare afbeeldingen te detecteren, waarmee je je bibliotheek kunt opschonen. Beschikbaar in Instellingen->Backup->Ruimte vrijmaken",
"cLTitle2": "Handmatige video stream generatie",
"cLDesc2": "Je kunt nu handmatig stream generatie voor video's activeren direct vanuit de app. We hebben ook een nieuw video streaming instellingenscherm toegevoegd dat toont welk percentage van je video's is verwerkt voor streaming",
"cLTitle3": "Prestatie Verbeteringen",
"cLDesc3": "Meerdere verbeteringen onder de motorkap, inclusief beter cache gebruik en een vloeiendere scroll ervaring"
}

View File

@@ -1736,5 +1736,11 @@
"albumsWidgetDesc": "Velg albumene du ønsker å se på din hjemskjerm.",
"memoriesWidgetDesc": "Velg typen minner du ønsker å se på din hjemskjerm.",
"smartMemories": "Smarte minner",
"pastYearsMemories": "Tidligere års minner"
"pastYearsMemories": "Tidligere års minner",
"cLTitle1": "Lignende Bilder",
"cLDesc1": "Vi introduserer et nytt ML-basert system for å oppdage lignende bilder, som du kan bruke til å rydde opp i biblioteket ditt. Tilgjengelig i Innstillinger->Sikkerhetskopi->Frigjør plass",
"cLTitle2": "Manuell video stream generering",
"cLDesc2": "Du kan nå manuelt utløse stream generering for videoer direkte fra appen. Vi har også lagt til en ny video streaming innstillinger skjerm som viser deg hvor mange prosent av videoene dine som er behandlet for streaming",
"cLTitle3": "Ytelsesforbedringar",
"cLDesc3": "Flere forbedringer under panseret, inkludert bedre cache bruk og en jevnere rullingsopplevelse"
}

View File

@@ -1776,14 +1776,6 @@
"same": "Identyczne",
"different": "Inne",
"sameperson": "Ta sama osoba?",
"cLTitle1": "Zaawansowany Edytor Obrazów",
"cLDesc1": "Wydajemy nowy i zaawansowany edytor obrazów, który dodaje więcej klatek przycinania, filtry dla szybkich edycji, precyzyjne opcje dostrajania, w tym nasycenie, kontrast, jasność, temperatura i wiele więcej. Nowy edytor zawiera również możliwość rysowania zdjęć i dodawania emotikonów jako naklejki.",
"cLTitle2": "Inteligentne Albumy",
"cLDesc2": "Teraz możesz automatycznie dodawać zdjęcia wybranych osób do dowolnego albumu. Po prostu przejdź do albumu i wybierz \"automatycznie dodaj osoby\" z menu przepełnienia. Jeśli używane razem z udostępnionym albumem, możesz udostępniać zdjęcia bez żadnych kliknięć.",
"cLTitle3": "Ulepszona Galeria",
"cLDesc3": "Dodaliśmy możliwość grupowania Twojej galerii po tygodniach, miesiącach i latach. Możesz teraz spersonalizować swoją galerię, aby dokładnie wyglądać w ten sposób z nowymi opcjami grupowania, wraz z niestandardowymi siatkami",
"cLTitle4": "Szybsze Przewijanie",
"cLDesc4": "Wraz z kilkoma ulepszeniami w celu poprawy doświadczenia galerii, przeprojektowaliśmy również pasek przewijania, aby pokazywać znaczniki, umożliwiając szybki skok po osi czasu.",
"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.",
"thisWeek": "Ten tydzień",
"lastWeek": "Zeszły tydzień",
@@ -1827,5 +1819,11 @@
"type": "int"
}
}
}
},
"cLTitle1": "Podobne Obrazy",
"cLDesc1": "Wprowadzamy nowy system oparty na ML do wykrywania podobnych obrazów, za pomocą którego możesz posprzątać swoją bibliotekę. Dostępne w Ustawienia->Kopia zapasowa->Zwolnij miejsce",
"cLTitle2": "Ręczne generowanie strumienia wideo",
"cLDesc2": "Możesz teraz ręcznie wyzwolić generowanie strumienia dla filmów bezpośrednio z aplikacji. Dodaliśmy również nowy ekran ustawień streamingu wideo, który pokaże ci, jaki procent twoich filmów zostało przetworzonych do streamingu",
"cLTitle3": "Ulepszenia Wydajności",
"cLDesc3": "Liczne ulepszenia pod maską, w tym lepsze wykorzystanie pamięci podręcznej i płynniejsze przewijanie"
}

View File

@@ -1776,14 +1776,6 @@
"same": "Igual",
"different": "Diferente",
"sameperson": "Mesma pessoa?",
"cLTitle1": "Editor de Imagens Avançado",
"cLDesc1": "Estamos lançando um novo editor de fotos avançado que adiciona mais quadros de recorte, predefinições de filtro para edições rápidas, ajustes para afinação incluindo saturação, contraste, brilho, temperatura e mais. O novo editor também incluí a habilidade de desenhar em suas fotos e adicionar emojis como figurinhas.",
"cLTitle2": "Álbuns Inteligentes",
"cLDesc2": "Você agora pode adicionar automaticamente fotos de pessoas selecionadas para qualquer álbum. É só ir ao álbum, selecionar \"adicionar pessoa auto.\" no menu avançado. Se usado junto ao álbum compartilhado, você pode compartilhar fotos sem maior esforço.",
"cLTitle3": "Galeria Aprimorada",
"cLDesc3": "Adicionamos a habilidade de agrupar sua galeria por semanas, meses, e anos. Você pode personalizar sua galeria para parecer exatamente a maneira que desejar usando as novas opções de agrupamento, junto às grades personalizadas",
"cLTitle4": "Arrastar Rápido",
"cLDesc4": "Junto ao tanto de melhorias salva-vidas para melhorar a experiência de arraste na galeria, também redesenhamos a barra de deslize para exibir marcadores, permitindo você pular a timeline rapidamente.",
"indexingPausedStatusDescription": "A indexação foi pausada. Ela retomará automaticamente quando o dispositivo estiver pronto. O dispositivo é considerado pronto quando o nível de bateria, saúde da bateria, e estado térmico estejam num alcance saudável.",
"thisWeek": "Esta semana",
"lastWeek": "Semana passada",
@@ -1827,5 +1819,11 @@
"type": "int"
}
}
}
}
},
"cLTitle1": "Imagens Similares",
"cLDesc1": "Estamos introduzindo um novo sistema baseado em ML para detectar imagens similares, com o qual você pode limpar sua biblioteca. Disponível em Configurações->Backup->Liberar Espaço",
"cLTitle2": "Geração manual de stream de vídeo",
"cLDesc2": "Agora você pode acionar manualmente a geração de stream para vídeos diretamente do aplicativo. Também adicionamos uma nova tela de configurações de streaming de vídeo que mostrará qual porcentagem dos seus vídeos foram processados para streaming",
"cLTitle3": "Melhorias de Performance",
"cLDesc3": "Múltiplas melhorias internas, incluindo melhor uso de cache e uma experiência de rolagem mais suave"
}

View File

@@ -1776,14 +1776,6 @@
"same": "Igual",
"different": "Diferente",
"sameperson": "A mesma pessoa?",
"cLTitle1": "Editor de Imagens Avançado",
"cLDesc1": "Estamos a lançar um novo editor avançado que adiciona mais ecrãs de recorte, predefinições de filtro para edições ágeis, ajustes de afinação incluindo saturação, contraste, brilho, temperatura e mais além. O novo editor também será possível desenhar nas suas fotos e adicionar emojis como autocolantes.",
"cLTitle2": "Álbuns Inteligentes",
"cLDesc2": "Agora pode automaticamente adicionar fotos de pessoas selecionadas para qualquer álbum. É só ir até o álbum, e clicar \"auto adicionar pessoa\" no menu expandido. Se usado com o álbum, pode partilhar fotos sem esforço.",
"cLTitle3": "Fototeca Improvisada",
"cLDesc3": "Adicionamos o agrupamento à sua fototeca, com filtro de semanas, meses, e anos. Pode personalizar a sua fototeca para parecer como desejar ao usar as novas definições de agrupamento, junto às grades personalizadas",
"cLTitle4": "Arraste Ágil",
"cLDesc4": "Junto às improvisações salva-vidas para melhorar a experiência de arraste na fototeca, também redesenhamos o slider para mostrar marcadores, permitindo você pular a linha do tempo mais fácil.",
"indexingPausedStatusDescription": "A indexação foi interrompida. Ele será retomado se o dispositivo estiver pronto. O dispositivo é considerado pronto se o nível de bateria, saúde da bateria, e estado térmico esteja num estado saudável.",
"thisWeek": "Esta semana",
"lastWeek": "Semana passada",
@@ -1827,5 +1819,11 @@
"type": "int"
}
}
}
}
},
"cLTitle1": "Imagens Similares",
"cLDesc1": "Estamos a introduzir um novo sistema baseado em ML para detectar imagens similares, com o qual pode limpar a sua biblioteca. Disponível em Definições->Cópia de Segurança->Libertar Espaço",
"cLTitle2": "Geração manual de stream de vídeo",
"cLDesc2": "Agora pode accionar manualmente a geração de stream para vídeos directamente da aplicação. Também adicionámos um novo ecrã de definições de streaming de vídeo que mostrará que percentagem dos seus vídeos foram processados para streaming",
"cLTitle3": "Melhorias de Performance",
"cLDesc3": "Múltiplas melhorias internas, incluindo melhor uso de cache e uma experiência de deslocação mais suave"
}

View File

@@ -1521,5 +1521,11 @@
"joinAlbum": "Alăturați-vă albumului",
"joinAlbumSubtext": "pentru a vedea și a adăuga fotografii",
"joinAlbumSubtextViewer": "pentru a adăuga la albumele distribuite",
"join": "Alăturare"
"join": "Alăturare",
"cLTitle1": "Imagini Similare",
"cLDesc1": "Introducem un nou sistem bazat pe ML pentru detectarea imaginilor similare, cu care vă puteți curăța biblioteca. Disponibil în Setări->Backup->Eliberați Spațiu",
"cLTitle2": "Generarea manuală a fluxului video",
"cLDesc2": "Acum puteți declanșa manual generarea fluxului pentru videoclipuri direct din aplicație. Am adăugat, de asemenea, un nou ecran de setări pentru streaming video care vă va arăta ce procent din videoclipurile dvs. au fost procesate pentru streaming",
"cLTitle3": "Îmbunătățiri de Performanță",
"cLDesc3": "Multiple îmbunătățiri în fundal, inclusiv o utilizare mai bună a cache-ului și o experiență de defilare mai fluidă"
}

View File

@@ -1785,5 +1785,11 @@
"analysis": "Анализ",
"day": "День",
"filter": "Фильтр",
"font": "Шрифт"
"font": "Шрифт",
"cLTitle1": "Похожие Изображения",
"cLDesc1": "Мы внедряем новую систему на основе ML для обнаружения похожих изображений, с помощью которой вы можете очистить свою библиотеку. Доступно в Настройки->Резервная копия->Освободить место",
"cLTitle2": "Ручная генерация видео потока",
"cLDesc2": "Теперь вы можете вручную запустить генерацию потока для видео прямо из приложения. Мы также добавили новый экран настроек видео стриминга, который покажет вам, какой процент ваших видео был обработан для стриминга",
"cLTitle3": "Улучшения Производительности",
"cLDesc3": "Множественные улучшения под капотом, включая лучшее использование кэша и более плавную прокрутку"
}

View File

@@ -347,9 +347,11 @@
"deletePhotos": "Radera foton",
"inviteToEnte": "Bjud in till Ente",
"removePublicLink": "Ta bort publik länk",
"disableLinkMessage": "Detta kommer att ta bort den publika länken för att komma åt \"{albumName}\".",
"sharing": "Delar...",
"youCannotShareWithYourself": "Du kan inte dela med dig själv",
"archive": "Arkiv",
"createAlbumActionHint": "Långtryck för att välja foton och klicka på + för att skapa ett album",
"importing": "Importerar....",
"failedToLoadAlbums": "Det gick inte att läsa in album",
"hidden": "Dold",
@@ -360,16 +362,96 @@
"videoSmallCase": "video",
"photoSmallCase": "foto",
"singleFileDeleteHighlight": "Det kommer att tas bort från alla album.",
"singleFileInBothLocalAndRemote": "Denna {fileType} finns i både Ente och din enhet.",
"singleFileInRemoteOnly": "Denna {fileType} kommer att raderas från Ente.",
"singleFileDeleteFromDevice": "Denna {fileType} kommer att raderas från din enhet.",
"deleteFromEnte": "Radera från ente",
"yesDelete": "Ja, radera",
"movedToTrash": "Flyttad till papperskorgen",
"deleteFromDevice": "Radera från enhet",
"deleteFromBoth": "",
"newAlbum": "Nytt album",
"albums": "Album",
"memoryCount": "{count, plural, =0{inga minnen} one{{formattedCount} minne} other{{formattedCount} minnen}}",
"@memoryCount": {
"description": "The text to display the number of memories",
"type": "text",
"placeholders": {
"count": {
"example": "1",
"type": "int"
},
"formattedCount": {
"type": "String",
"example": "11.513, 11,511"
}
}
},
"selectedPhotos": "{count} markerade",
"@selectedPhotos": {
"description": "Display the number of selected photos",
"type": "text",
"placeholders": {
"count": {
"example": "5",
"type": "int"
}
}
},
"selectedPhotosWithYours": "{count} markerade ({yourCount} din)",
"@selectedPhotosWithYours": {
"description": "Display the number of selected photos, including the number of selected photos owned by the user",
"type": "text",
"placeholders": {
"count": {
"example": "12",
"type": "int"
},
"yourCount": {
"example": "2",
"type": "int"
}
}
},
"advancedSettings": "Avancerad",
"@advancedSettings": {
"description": "The text to display in the advanced settings section"
},
"photoGridSize": "Storlek på bildrutnät",
"manageDeviceStorage": "Hantera enhetscache",
"manageDeviceStorageDesc": "Granska och rensa lokal cachelagring.",
"machineLearning": "Maskininlärning",
"mlConsent": "Aktivera maskininlärning",
"mlConsentTitle": "Aktivera maskininlärning?",
"mlConsentDescription": "Om du aktiverar maskininlärning, kommer Ente extrahera information som ansiktsgeometri från filer, inklusive de som delas med dig.\n\nDetta kommer att hända på din enhet, och all genererad biometrisk information kommer att helsträckskrypteras.",
"mlConsentPrivacy": "Klicka här för mer information om denna funktion i vår integritetspolicy",
"mlConsentConfirmation": "Jag förstår och vill aktivera maskininlärning",
"magicSearch": "Magisk sökning",
"discover": "Upptäck",
"@discover": {
"description": "The text to display for the discover section under which we show receipts, screenshots, sunsets, greenery, etc."
},
"discover_identity": "Identitet",
"discover_screenshots": "Skärmdumpar",
"discover_receipts": "Kvitton",
"discover_notes": "Anteckningar",
"discover_memes": "Memes",
"discover_visiting_cards": "Besökskort",
"discover_babies": "Barn",
"discover_pets": "Husdjur",
"discover_selfies": "Selfies",
"discover_wallpapers": "Bakgrundsbilder",
"discover_food": "Mat",
"discover_celebrations": "Firanden",
"discover_sunset": "Solnedgång",
"discover_hills": "Berg",
"discover_greenery": "Grönt landskap",
"mlIndexingDescription": "Observera att maskininlärning kommer att resultera i en högre bandbredd och batterianvändning tills alla objekt är indexerade. Överväg att använda skrivbordsappen för snabbare indexering, alla resultat kommer att synkroniseras automatiskt.",
"loadingModel": "Laddar ner modeller...",
"waitingForWifi": "Väntar på WiFi...",
"status": "Status",
"indexedItems": "Indexerade objekt",
"pendingItems": "Väntande objekt",
"clearIndexes": "Rensa index",
"selectFoldersForBackup": "Välj mappar för säkerhetskopiering",
"selectedFoldersWillBeEncryptedAndBackedUp": "Valda mappar kommer att krypteras och säkerhetskopieras",
@@ -398,19 +480,47 @@
"yearsAgo": "{count, plural, one{{count} år sedan} other{{count} år sedan}}",
"backupSettings": "Säkerhetskopieringsinställningar",
"backupStatus": "Säkerhetskopieringsstatus",
"backupStatusDescription": "Objekt som har säkerhetskopierats kommer att visas här",
"backupOverMobileData": "Säkerhetskopiera via mobildata",
"backupVideos": "Säkerhetskopiera videor",
"disableAutoLock": "Inaktivera automatisk låsning",
"deviceLockExplanation": "Inaktivera enhetens skärmlås när Ente är i förgrunden och säkerhetskopiering pågår. Detta är normalt inte nödvändigt, men kan hjälpa stora uppladdningar och initial import av stora bibliotek slutföra snabbare.",
"about": "Om",
"weAreOpenSource": "Vi har öppen källkod!",
"privacy": "Sekretess",
"terms": "Villkor",
"checkForUpdates": "Sök efter uppdateringar",
"checkStatus": "Kontrollera status",
"checking": "Kontrollerar...",
"youAreOnTheLatestVersion": "Du är på den senaste versionen",
"account": "Konto",
"manageSubscription": "Hantera prenumeration",
"authToChangeYourEmail": "Autentisera för att ändra din e-postadress",
"changePassword": "Ändra lösenord",
"authToChangeYourPassword": "Autentisera för att ändra ditt lösenord",
"emailVerificationToggle": "E-postverifiering",
"exportYourData": "Exportera din data",
"logout": "Logga ut",
"authToInitiateAccountDeletion": "Vänligen autentisera för att initiera borttagning av konto",
"areYouSureYouWantToLogout": "Är du säker på att du vill logga ut?",
"yesLogout": "Ja, logga ut",
"aNewVersionOfEnteIsAvailable": "En ny version av Ente är tillgänglig.",
"update": "Uppdatera",
"installManually": "Installera manuellt",
"criticalUpdateAvailable": "Kritisk uppdatering tillgänglig",
"updateAvailable": "Uppdatering tillgänglig",
"ignoreUpdate": "Ignorera",
"downloading": "Laddar ner...",
"cannotDeleteSharedFiles": "Kan inte ta bort delade filer",
"theDownloadCouldNotBeCompleted": "Nedladdningen kunde inte slutföras",
"retry": "Försök igen",
"backedUpFolders": "Säkerhetskopiera mappar",
"backup": "Säkerhetskopiera",
"freeUpDeviceSpace": "Frigör enhetens lagringsutrymme",
"freeUpDeviceSpaceDesc": "Spara utrymme på din enhet genom att rensa filer som redan har säkerhetskopierats.",
"allClear": "✨ Allt klart",
"noDeviceThatCanBeDeleted": "Du har inga filer på denna enhet som kan tas bort",
"removeDuplicates": "Ta bort dubbletter",
"viewActiveSessions": "Visa aktiva sessioner",
"no": "Nej",
"yes": "Ja",

View File

@@ -1776,5 +1776,11 @@
"same": "Aynı",
"different": "Farklı",
"sameperson": "Aynı kişi mi?",
"indexingPausedStatusDescription": "Dizin oluşturma duraklatıldı. Cihaz hazır olduğunda otomatik olarak devam edecektir. Cihaz, pil seviyesi, pil sağlığı ve termal durumu sağlıklı bir aralıkta olduğunda hazır kabul edilir."
"indexingPausedStatusDescription": "Dizin oluşturma duraklatıldı. Cihaz hazır olduğunda otomatik olarak devam edecektir. Cihaz, pil seviyesi, pil sağlığı ve termal durumu sağlıklı bir aralıkta olduğunda hazır kabul edilir.",
"cLTitle1": "Benzer Görüntüler",
"cLDesc1": "Benzer görüntüleri tespit etmek için yeni bir ML tabanlı sistem tanıtıyoruz, bununla kütüphanenizi temizleyebilirsiniz. Ayarlar->Yedekleme->Alan Boşalt kısmından ulaşabilirsiniz",
"cLTitle2": "Manuel video akış oluşturma",
"cLDesc2": "Artık doğrudan uygulamadan videolar için akış oluşturmayı manuel olarak tetikleyebilirsiniz. Ayrıca videolarınızın yüzde kaçının akış için işlendiğini gösteren yeni bir video akış ayarları ekranı da ekledik",
"cLTitle3": "Performans İyileştirmeleri",
"cLDesc3": "Daha iyi önbellek kullanımı ve daha pürüzsüz kaydırma deneyimi dahil olmak üzere perde arkasında birçok iyileştirme"
}

View File

@@ -1509,5 +1509,11 @@
},
"legacyInvite": "{email} запросив вас стати довіреною особою",
"authToManageLegacy": "Авторизуйтесь, щоби керувати довіреними контактами",
"useDifferentPlayerInfo": "Виникли проблеми з відтворенням цього відео? Натисніть і утримуйте тут, щоб спробувати інший плеєр."
"useDifferentPlayerInfo": "Виникли проблеми з відтворенням цього відео? Натисніть і утримуйте тут, щоб спробувати інший плеєр.",
"cLTitle1": "Схожі Зображення",
"cLDesc1": "Ми впроваджуємо нову систему на основі ML для виявлення схожих зображень, за допомогою якої ви можете очистити свою бібліотеку. Доступно в Налаштування->Резервна копія->Звільнити місце",
"cLTitle2": "Ручна генерація відео потоку",
"cLDesc2": "Тепер ви можете вручну запустити генерацію потоку для відео прямо з додатку. Ми також додали новий екран налаштувань відео стрімінгу, який покаже вам, який відсоток ваших відео було оброблено для стрімінгу",
"cLTitle3": "Покращення Продуктивності",
"cLDesc3": "Численні покращення під капотом, включаючи краще використання кешу та більш плавну прокрутку"
}

View File

@@ -1776,14 +1776,6 @@
"same": "Chính xác",
"different": "Khác",
"sameperson": "Cùng một người?",
"cLTitle1": "Trình chỉnh sửa ảnh nâng cao",
"cLDesc1": "Chúng tôi phát hành một trình chỉnh sửa ảnh tân tiến, bổ sung thêm cắt ảnh, bộ lọc có sẵn để chỉnh sửa nhanh, các tùy chọn tinh chỉnh bao gồm độ bão hòa, độ tương phản, độ sáng, độ ấm và nhiều hơn nữa. Trình chỉnh sửa mới cũng bao gồm khả năng vẽ lên ảnh và thêm emoji dưới dạng nhãn dán.",
"cLTitle2": "Album thông minh",
"cLDesc2": "Giờ đây, bạn có thể tự động thêm ảnh của những người đã chọn vào bất kỳ album nào. Chỉ cần mở album và chọn \"Tự động thêm người\" trong menu. Nếu sử dụng cùng với album chia sẻ, bạn có thể chia sẻ ảnh mà không cần tốn công.",
"cLTitle3": "Cải tiến Thư viện ảnh",
"cLDesc3": "Chúng tôi bổ sung tính năng phân nhóm thư viện ảnh theo tuần, tháng và năm. Giờ đây, bạn có thể tùy chỉnh thư viện ảnh theo đúng ý muốn với các tùy chọn mới này, cùng với các lưới tùy chỉnh.",
"cLTitle4": "Cuộn nhanh hơn",
"cLDesc4": "Cùng với một loạt cải tiến ngầm nhằm nâng cao trải nghiệm cuộn thư viện, chúng tôi cũng đã thiết kế lại thanh cuộn để hiển thị các điểm đánh dấu, cho phép bạn nhanh chóng nhảy cóc trên dòng thời gian.",
"indexingPausedStatusDescription": "Lập chỉ mục bị tạm dừng. Nó sẽ tự động tiếp tục khi thiết bị đã sẵn sàng. Thiết bị được coi là sẵn sàng khi mức pin, tình trạng pin và trạng thái nhiệt độ nằm trong phạm vi tốt.",
"thisWeek": "Tuần này",
"lastWeek": "Tuần trước",
@@ -1827,5 +1819,123 @@
"type": "int"
}
}
}
}
},
"videosProcessed": "Video đã được xử lý",
"totalVideos": "Tổng số video",
"skippedVideos": "Video bị bỏ qua",
"videoStreamingDescriptionLine1": "Phát video trên bất kỳ thiết bị.",
"videoStreamingDescriptionLine2": "Bật để xử lý luồng phát video trên thiết bị này.",
"videoStreamingNote": "Thiết bị này chỉ xử lý các video từ 60 ngày trở xuống và có thời lượng dưới 1 phút. Đối với các video cũ hơn/dài hơn, hãy bật tính năng phát trực tuyến trong ứng dụng máy tính để bàn.",
"createStream": "Tạo phát trực tiếp",
"recreateStream": "Tạo lại phát trực tiếp",
"addedToStreamCreationQueue": "Đã thêm vào hàng đợi tạo luồng",
"addedToStreamRecreationQueue": "Đã thêm vào hàng đợi tạo lại luồng",
"videoPreviewAlreadyExists": "Bản xem trước video đã tồn tại",
"videoAlreadyInQueue": "Tệp video đã có trong hàng đợi",
"addedToQueue": "Đã thêm vào hàng đợi",
"creatingStream": "Đang tạo luồng",
"similarImages": "Ảnh giống nhau",
"findSimilarImages": "Tìm ảnh giống nhau",
"noSimilarImagesFound": "Không tìm thấy ảnh giống nhau",
"yourPhotosLookUnique": "Ảnh của bạn trông độc đáo",
"similarGroupsFound": "{count, plural, =1{{count} nhóm được tìm thấy} other{{count} nhóm được tìm thấy}}",
"@similarGroupsFound": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"reviewAndRemoveSimilarImages": "Xem lại và xóa ảnh giống nhau",
"deletePhotosWithSize": "Xóa {count} ảnh ({size})",
"@deletePhotosWithSize": {
"placeholders": {
"count": {
"type": "String"
},
"size": {
"type": "String"
}
}
},
"selectionOptions": "Tùy chọn lựa chọn",
"selectExactWithCount": "Giống nhau hoàn toàn ({count})",
"@selectExactWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectExact": "Chọn chính xác",
"selectSimilarWithCount": "Giống nhau một phần ({count})",
"@selectSimilarWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectSimilar": "Chọn giống nhau",
"selectAllWithCount": "Tất cả giống nhau ({count})",
"@selectAllWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectSimilarImagesTitle": "Chọn những ảnh giống nhau",
"chooseSimilarImagesToSelect": "Chọn ảnh dựa trên sự tương đồng thị giác",
"clearSelection": "Bỏ chọn",
"similarImagesCount": "{count} ảnh giống nhau",
"@similarImagesCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"deleteWithCount": "Xóa ({count})",
"@deleteWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"deleteFiles": "Xóa các tệp",
"areYouSureDeleteFiles": "Bạn có chắc muốn xóa các tệp này?",
"greatJob": "Tốt lắm!",
"cleanedUpSimilarImages": "Bạn tiết kiệm được {size}",
"@cleanedUpSimilarImages": {
"placeholders": {
"size": {
"type": "String"
}
}
},
"size": "Dung lượng",
"similarity": "Sự giống nhau",
"analyzingPhotosLocally": "Phân tích ảnh của bạn trên thiết bị...",
"lookingForVisualSimilarities": "Tìm theo sự tương đồng thị giác...",
"comparingImageDetails": "So sánh các đặc điểm ảnh...",
"findingSimilarImages": "Tìm các ảnh giống nhau...",
"almostDone": "Sắp xong...",
"processingLocally": "Đang xử lý cục bộ",
"useMLToFindSimilarImages": "Xem lại và xóa những ảnh có vẻ giống nhau.",
"all": "Tất cả",
"similar": "Giống nhau",
"identical": "Giống hệt nhau",
"nothingHereTryAnotherFilter": "Không thấy gì, hãy thử thay đổi bộ lọc! 👀",
"related": "Có liên quan",
"hoorayyyy": "Hoorayyyy!",
"nothingToTidyUpHere": "Ở đây đã ngon lành rồi",
"deletingDash": "Đang xóa - ",
"cLTitle1": "Hình Ảnh Tương Tự",
"cLDesc1": "Chúng tôi đang giới thiệu một hệ thống dựa trên ML mới để phát hiện hình ảnh tương tự, bạn có thể dùng để dọn dẹp thư viện của mình. Có sẵn trong Cài đặt->Sao lưu->Giải phóng Dung lượng",
"cLTitle2": "Tạo luồng video thủ công",
"cLDesc2": "Bây giờ bạn có thể kích hoạt tạo luồng cho video trực tiếp từ ứng dụng. Chúng tôi cũng đã thêm màn hình cài đặt phát trực tuyến video mới sẽ cho bạn biết bao nhiêu phần trăm video của bạn đã được xử lý để phát trực tuyến",
"cLTitle3": "Cải Thiện Hiệu Suất",
"cLDesc3": "Nhiều cải thiện bên trong, bao gồm sử dụng bộ nhớ đệm tốt hơn và trải nghiệm cuộn mượt mà hơn"
}

View File

@@ -1776,14 +1776,6 @@
"same": "相同",
"different": "不同",
"sameperson": "是同一个人?",
"cLTitle1": "高级图像编辑器",
"cLDesc1": "我们正在发布一款全新且高级的图像编辑器,新增更多裁剪框架、快速编辑的滤镜预设,以及包括饱和度、对比度、亮度、色温等在内的精细调整选项。新的编辑器还支持在照片上绘制和添加表情符号作为贴纸。",
"cLTitle2": "智能相册",
"cLDesc2": "您现在可以将所选人物的照片自动添加到任何相册。只需进入相册,从溢出菜单中选择“自动添加人物”。如果与共享相册一起使用,您可以零点击分享照片。",
"cLTitle3": "改进的相册",
"cLDesc3": "我们新增了按周、月、年对图库进行分组的功能。您现在可以通过这些新的分组选项以及自定义网格,定制图库的外观,完全按照您的喜好进行设置",
"cLTitle4": "更快滚动",
"cLDesc4": "除了多项后台改进以提升图库滚动体验外,我们还重新设计了滚动条,添加了标记功能,让您可以快速跳转到时间轴上的不同位置。",
"indexingPausedStatusDescription": "索引已暂停。待设备准备就绪后,索引将自动恢复。当设备的电池电量、电池健康度和温度状态处于健康范围内时,设备即被视为准备就绪。",
"thisWeek": "本周",
"lastWeek": "上周",
@@ -1827,5 +1819,117 @@
"type": "int"
}
}
}
}
},
"videosProcessed": "视频已处理",
"totalVideos": "视频总数",
"skippedVideos": "已跳过的视频",
"videoStreamingDescriptionLine1": "在任何设备上立即播放视频。",
"videoStreamingDescriptionLine2": "启用以处理此设备上的视频流。",
"videoStreamingNote": "此设备仅处理过去 60 天内时长不超过 1 分钟的视频。对于更早/更长的视频,请在桌面应用中启用流式传输。",
"createStream": "创建流",
"recreateStream": "重建流",
"addedToStreamCreationQueue": "已添加到流创建队列",
"addedToStreamRecreationQueue": "已添加到流重建队列",
"videoPreviewAlreadyExists": "视频预览已存在",
"videoAlreadyInQueue": "视频文件已存在于队列中",
"addedToQueue": "已添加至队列",
"creatingStream": "正在创建流",
"similarImages": "相似图片",
"findSimilarImages": "查找相似图片",
"noSimilarImagesFound": "未找到相似图片",
"yourPhotosLookUnique": "您的照片看起来很独特",
"similarGroupsFound": "{count, plural, =1{{count} 组已找到} other{{count} 组已找到}}",
"@similarGroupsFound": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"reviewAndRemoveSimilarImages": "查看并删除相似图片",
"deletePhotosWithSize": "删除 {count} 张照片 ({size})",
"@deletePhotosWithSize": {
"placeholders": {
"count": {
"type": "String"
},
"size": {
"type": "String"
}
}
},
"selectionOptions": "选择选项",
"selectExactWithCount": "完全相似 ({count})",
"@selectExactWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectExact": "精确选择",
"selectSimilarWithCount": "部分相似 ({count})",
"@selectSimilarWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectSimilar": "选择相似项",
"selectAllWithCount": "所有相似项 ({count})",
"@selectAllWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"selectSimilarImagesTitle": "选择所有相似图片",
"chooseSimilarImagesToSelect": "根据视觉相似性选择图像",
"clearSelection": "清除选择",
"similarImagesCount": "{count} 张相似图片",
"@similarImagesCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"deleteWithCount": "删除 ({count}) 项",
"@deleteWithCount": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"deleteFiles": "删除文件",
"areYouSureDeleteFiles": "您确定要删除这些文件吗?",
"greatJob": "做得好!",
"cleanedUpSimilarImages": "您已释放了 {size} 的空间",
"@cleanedUpSimilarImages": {
"placeholders": {
"size": {
"type": "String"
}
}
},
"size": "大小",
"similarity": "相似度",
"processingLocally": "正在本地处理",
"useMLToFindSimilarImages": "审查并删除看起来彼此相似的图像。",
"all": "全部",
"similar": "相似的",
"identical": "完全相同",
"nothingHereTryAnotherFilter": "此处无内容,请尝试其他过滤器!👀",
"related": "相关",
"hoorayyyy": "耶~~!",
"nothingToTidyUpHere": "这里没什么可清理的",
"cLTitle1": "相似图像",
"cLDesc1": "我们正在推出一个基于机器学习的新系统来检测相似图像,您可以用它来清理您的图库。在 设置->备份->释放空间 中可用",
"cLTitle2": "手动视频流生成",
"cLDesc2": "您现在可以直接从应用程序手动触发视频的流生成。我们还添加了一个新的视频流设置屏幕,它将显示您的视频中有百分之几已被处理用于流媒体播放",
"cLTitle3": "性能改进",
"cLDesc3": "多个底层改进,包括更好的缓存使用和更流畅的滚动体验"
}

View File

@@ -2,6 +2,29 @@ import "package:flutter/widgets.dart";
import 'package:photos/generated/intl/app_localizations.dart';
import "package:shared_preferences/shared_preferences.dart";
// list of locales which are enabled for photos app.
// Add more language to the list only when at least 90% of the strings are
// translated in the corresponding language.
const List<Locale> appSupportedLocales = <Locale>[
Locale('en'),
Locale('es'),
Locale('de'),
Locale('fr'),
Locale('it'),
Locale('ja'),
Locale("nl"),
Locale("no"),
Locale("pl"),
Locale("pt", "BR"),
Locale('pt', 'PT'),
Locale("ro"),
Locale("ru"),
Locale("tr"),
Locale("uk"),
Locale("vi"),
Locale("zh", "CN"),
];
extension AppLocalizationsX on BuildContext {
AppLocalizations get l10n => AppLocalizations.of(this);
}
@@ -12,12 +35,12 @@ Locale? autoDetectedLocale;
Locale localResolutionCallBack(deviceLocales, supportedLocales) {
_onDeviceLocales = deviceLocales;
final Set<String> languageSupport = {};
for (Locale supportedLocale in AppLocalizations.supportedLocales) {
for (Locale supportedLocale in appSupportedLocales) {
languageSupport.add(supportedLocale.languageCode);
}
for (Locale locale in deviceLocales) {
// check if exact local is supported, if yes, return it
if (AppLocalizations.supportedLocales.contains(locale)) {
if (appSupportedLocales.contains(locale)) {
autoDetectedLocale = locale;
return locale;
}
@@ -67,7 +90,7 @@ Future<Locale?> getLocale({
} else {
savedLocale = Locale(savedValue);
}
if (AppLocalizations.supportedLocales.contains(savedLocale)) {
if (appSupportedLocales.contains(savedLocale)) {
return savedLocale;
}
}
@@ -81,7 +104,7 @@ Future<Locale?> getLocale({
}
Future<void> setLocale(Locale locale) async {
if (!AppLocalizations.supportedLocales.contains(locale)) {
if (!appSupportedLocales.contains(locale)) {
throw Exception('Locale $locale is not supported by the app');
}
final StringBuffer out = StringBuffer(locale.languageCode);

View File

@@ -280,7 +280,6 @@ Future<void> _init(bool isBackground, {String via = ''}) async {
}).ignore();
}
_logger.info("PushService/HomeWidget done $tlog");
VideoPreviewService.instance.init(preferences);
unawaited(SemanticSearchService.instance.init());
unawaited(MLService.instance.init());
await PersonService.init(

View File

@@ -13,7 +13,8 @@ class Collection {
final User owner;
final String encryptedKey;
final String? keyDecryptionNonce;
@Deprecated("Use collectionName instead")
/// WARNING: use collectionName instead of name! Name is deprecated but can't be removed because of old accounts.
String? name;
// encryptedName & nameDecryptionNonce will be null for collections

View File

@@ -0,0 +1,44 @@
import 'package:photos/models/faces_through_time/face_timeline_entry.dart';
class FaceTimeline {
final String personId;
final List<FaceTimelineEntry> entries;
final DateTime generatedAt;
final bool hasBeenViewed;
final int version;
FaceTimeline({
required this.personId,
required this.entries,
required this.generatedAt,
this.hasBeenViewed = false,
this.version = 1,
});
Map<String, dynamic> toJson() => {
'personId': personId,
'generatedAt': generatedAt.toIso8601String(),
'faceIds': entries.map((e) => e.faceId).toList(),
'hasBeenViewed': hasBeenViewed,
'version': version,
};
factory FaceTimeline.fromJson(Map<String, dynamic> json) {
return FaceTimeline(
personId: json['personId'],
entries: (json['faceIds'] as List)
.map((id) => FaceTimelineEntry(faceId: id))
.toList(),
generatedAt: DateTime.parse(json['generatedAt']),
hasBeenViewed: json['hasBeenViewed'] ?? false,
version: json['version'] ?? 1,
);
}
bool get isValid {
final age = DateTime.now().difference(generatedAt);
return age.inDays < 365; // Cache valid for 1 year
}
int get totalFaces => entries.length;
}

View File

@@ -0,0 +1,44 @@
import 'dart:typed_data';
class FaceTimelineEntry {
final String faceId;
final int fileId;
final DateTime timestamp;
final String? ageText;
final String? relativeTimeText;
Uint8List? thumbnail;
FaceTimelineEntry({
required this.faceId,
this.fileId = 0,
DateTime? timestamp,
this.ageText,
this.relativeTimeText,
this.thumbnail,
}) : timestamp = timestamp ?? DateTime.now();
String get displayText => ageText ?? relativeTimeText ?? '';
bool get hasThumbnail => thumbnail != null;
// For JSON serialization
Map<String, dynamic> toJson() => {
'faceId': faceId,
'fileId': fileId,
'timestamp': timestamp.toIso8601String(),
'ageText': ageText,
'relativeTimeText': relativeTimeText,
};
factory FaceTimelineEntry.fromJson(Map<String, dynamic> json) {
return FaceTimelineEntry(
faceId: json['faceId'],
fileId: json['fileId'] ?? 0,
timestamp: json['timestamp'] != null
? DateTime.parse(json['timestamp'])
: DateTime.now(),
ageText: json['ageText'],
relativeTimeText: json['relativeTimeText'],
);
}
}

View File

@@ -1,3 +1,4 @@
// https://discover.ente.io/v1.json
class Prompt {
final String id;
final int position;
@@ -8,17 +9,6 @@ class Prompt {
final bool showVideo;
final bool recentFirst;
Prompt({
String? id,
this.position = 0,
required this.query,
required this.minScore,
required this.minSize,
required this.title,
this.showVideo = true,
this.recentFirst = false,
}) : id = id ?? title;
// fromJson
Prompt.fromJson(Map<String, dynamic> json)
: id = json['id'] ?? json['title'],

View File

@@ -15,7 +15,6 @@ class SimilarFiles {
int get totalSize => files.fold(0, (sum, file) => sum + (file.fileSize ?? 0));
// TODO: lau: check if we're not using this wrong
bool get isEmpty => files.isEmpty;
int get length => files.length;

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