Compare commits

...

21 Commits

Author SHA1 Message Date
Daniel Dietzler d98aecf82a fix: detail panel people reactivity and iterator consumption 2026-06-22 13:39:35 +02:00
Matthew Momjian dc7d57ff9a fix(docsc): v3 bump (#29246)
v3 bump in docs
2026-06-21 20:44:58 -05:00
Alex b24a617142 chore: bump mobile build (#29215) 2026-06-19 12:50:20 -05:00
Mees Frensel 62b00a1f26 refactor: slideshow and setalbumcover actions (#29211)
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
2026-06-19 13:54:33 +00:00
Timon 95fc5e9682 docs: clarify duplicate exif merging intent (#29203) 2026-06-19 10:57:35 +02:00
github-actions 38920fc4ca chore: version v3.0.0-rc.2 2026-06-18 23:20:14 +00:00
Weblate (bot) 3abeb4df92 chore(web): update translations (#29162)
Translate-URL: https://hosted.weblate.org/projects/immich/immich/be/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/bg/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/cs/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/de/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/eo/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/es/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/eu/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/gl/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/hu/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/it/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ko/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/pt/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/sk/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/sl/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/sv/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/th/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/tr/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/yue_Hant/
Translation: Immich/immich

Co-authored-by: Erik Sikander <erik.sikander@gmail.com>
Co-authored-by: Fjuro <fjuro@users.noreply.hosted.weblate.org>
Co-authored-by: Frank Paul Silye <frankps@gmail.com>
Co-authored-by: HackingAll <hacking.all.YT@gmail.com>
Co-authored-by: Hurricane_32 <rodrigorimo@hotmail.com>
Co-authored-by: Ivan Dimitrov <idimitrov08@gmail.com>
Co-authored-by: Jayden Lo <jaydenlo08@gmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Matjaž T. <matjaz@moj-svet.si>
Co-authored-by: Muxutruk <156070698+Muxutruk2@users.noreply.github.com>
Co-authored-by: Muxutruk <benat.eigurenzu@elorrieta-errekamari.com>
Co-authored-by: Nagy Krisztián <nkgy17@gmail.com>
Co-authored-by: Nicola Bortoletto <nicola.bortoletto@live.com>
Co-authored-by: PPNplus <ppnplus@protonmail.com>
Co-authored-by: Pavel Miniutka <pavel.miniutka@gmail.com>
Co-authored-by: Tim Morley <weblate.3919org@timsk.org>
Co-authored-by: Zekai Şahin <zekainihas@gmail.com>
Co-authored-by: bittin1ddc447d824349b2 <bittin@reimu.nl>
Co-authored-by: bones78 <jens@mueller-starck.de>
Co-authored-by: 김도윤 <wezardnote@gmail.com>
2026-06-18 23:17:29 +00:00
Daniel Dietzler 805bb84877 fix: defensive album owner migration (#29200) 2026-06-18 23:51:38 +02:00
Daniel Dietzler a719552243 fix: rc version check (#29194) 2026-06-18 21:00:12 +00:00
Santo Shakil 9a5e7a8e47 fix(mobile): endless spinner on album selection when device has no albums (#28994)
* fix(mobile): endless spinner on album selection when device has no albums

* use a page scoped future provider for the loading state

* refactor(mobile): decide album selection empty state in the parent
2026-06-18 21:38:15 +05:30
Daniel Dietzler 62c6bb27e3 fix: workflow asset type filter required (#29196) 2026-06-18 16:04:28 +00:00
renovate[bot] f3cb3cf98d fix(deps): update dependency nodemailer to v9 [security] (#29195) 2026-06-18 17:45:15 +02:00
Santo Shakil c35abb2f66 fix(mobile): re-lock locked folder when the app is backgrounded (#29089)
* fix(mobile): re-lock locked folder when the app is backgrounded

* fix(mobile): simplify locked folder overlay condition

* fix(mobile): lock locked folder on pause instead of resume
2026-06-18 21:10:18 +05:30
Santo Shakil 793487e52c fix(mobile): refresh memories on resume and day change (#28983)
* fix(mobile): refresh memories on resume and day change

* fix(mobile): invalidate memories once after the resume sync
2026-06-18 20:46:20 +05:30
Ebin Santhosh 769c4015d3 fix(mobile): prevent duplicate login pages for unauthenticated share intent warm start (#29054) 2026-06-18 20:45:35 +05:30
renovate[bot] c07cbe7ca8 chore(deps): update dependency multer to v2.2.0 [security] (#29183) 2026-06-18 16:31:10 +02:00
Timon 6d73bf4e36 refactor(web): move keyboard state into a store (#29181) 2026-06-18 16:29:26 +02:00
renovate[bot] 735f52a321 fix(deps): update typescript-projects (#29130)
Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2026-06-18 16:21:35 +02:00
Daniel Dietzler 53fe26593c fix: asset type filter (#29190) 2026-06-18 15:30:34 +02:00
Daniel Dietzler 40cffcd414 fix: remove local-only step ids from workflow json (#29188) 2026-06-18 08:54:35 -04:00
Timon 48861b085e chore(server): organize integrity dtos (#29191) 2026-06-18 14:44:28 +02:00
74 changed files with 1568 additions and 907 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ DB_DATA_LOCATION=./postgres
# TZ=Etc/UTC
# The Immich version to use. You can pin this to a specific version like "v2.1.0"
IMMICH_VERSION=v2
IMMICH_VERSION=v3
# Connection secret for postgres. You should change it to a random password
# Please use only the characters `A-Za-z0-9`, without special characters or spaces
+10 -10
View File
@@ -15,14 +15,14 @@ When using "Deduplicate All" or viewing suggestions, Immich automatically presel
### Synchronizing metadata
When resolving duplicates, metadata from trashed assets is automatically synchronized to the kept assets. The following metadata is synchronized:
When resolving duplicates, metadata from trashed assets is automatically synchronized to the kept asset. This synchronization only happens when **exactly one** asset is kept and at least one asset is trashed. When more than one asset is kept, metadata is not merged — the assets keep their own metadata and are simply removed from the duplicate group. The following metadata is synchronized:
| Name | Description |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Album | The kept assets will be added to _every_ album that the other assets in the group belong to. |
| Favorite | If any of the assets in the group have been added to favorites, every kept asset will also be added to favorites. |
| Rating | If one or more assets in the duplicate group have a rating, the highest rating is selected and synchronized to the kept assets. |
| Description | Descriptions from each asset are combined together and synchronized to all the kept assets. |
| Visibility | The most restrictive visibility is applied to the kept assets. |
| Location | Latitude and longitude are copied if all assets with geolocation data in the group share the same coordinates. |
| Tag | Tags from all assets in the group are merged and applied to every kept asset. |
| Name | Description |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Album | The kept asset will be added to _every_ album that the other assets in the group belong to. |
| Favorite | If any of the assets in the group have been added to favorites, the kept asset will also be added to favorites. |
| Rating | If one or more assets in the duplicate group have a rating, the highest rating is selected and synchronized to the kept asset. |
| Description | Descriptions from each asset are combined together and synchronized to the kept asset. |
| Visibility | The most restrictive visibility is applied to the kept asset. |
| Location | Latitude and longitude are copied if all assets with geolocation data in the group share the same coordinates. |
| Tag | Tags from all assets in the group are merged and applied to the kept asset. |
+1 -1
View File
@@ -19,7 +19,7 @@ If this does not work, try running `docker compose up -d --force-recreate`.
| Variable | Description | Default | Containers |
| :----------------- | :------------------------------ | :-----: | :----------------------- |
| `IMMICH_VERSION` | Image tags | `v2` | server, machine learning |
| `IMMICH_VERSION` | Image tags | `v3` | server, machine learning |
| `UPLOAD_LOCATION` | Host path for uploads | | server |
| `DB_DATA_LOCATION` | Host path for Postgres database | | database |
+1 -1
View File
@@ -29,7 +29,7 @@ docker image prune
## Versioning Policy
Immich follows [semantic versioning][semver], which tags releases in the format `<major>.<minor>.<patch>`. We intend for breaking changes to be limited to major version releases.
You can configure your Docker image to point to the current major version by using a metatag, such as `:v2`.
You can configure your Docker image to point to the current major version by using a metatag, such as `:v3`.
Currently, we have no plans to backport patches to earlier versions. We encourage all users to run the most recent release of Immich.
Switching back to an earlier version, even within the same minor release tag, is not supported.
+1 -1
View File
@@ -1,7 +1,7 @@
# @generated - this file is auto-generated by `mise lock` https://mise.en.dev/dev-tools/mise-lock.html
[[tools.wrangler]]
version = "4.98.0"
version = "4.100.0"
backend = "npm:wrangler"
[tools.wrangler.options]
+1 -1
View File
@@ -28,4 +28,4 @@ run = "prettier --write ."
run = "wrangler pages deploy build --project-name=${PROJECT_NAME} --branch=${BRANCH_NAME}"
[tools]
wrangler = "4.98.0"
wrangler = "4.100.0"
+2 -2
View File
@@ -1,7 +1,7 @@
[
{
"label": "v3.0.0-rc.1",
"url": "https://docs.v3.0.0-rc.1.archive.immich.app"
"label": "v3.0.0-rc.2",
"url": "https://docs.v3.0.0-rc.2.archive.immich.app"
},
{
"label": "v2.7.5",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "immich-e2e",
"version": "3.0.0-rc.1",
"version": "3.0.0-rc.2",
"description": "",
"main": "index.js",
"type": "module",
+3
View File
@@ -192,11 +192,14 @@
"maintenance_delete_backup": "Выдаліць рэзервовую копію",
"maintenance_delete_backup_description": "Гэты файл будзе беззваротна выдалены.",
"maintenance_delete_error": "Не атрымалася выдаліць рэзервовую копію.",
"maintenance_integrity_check": "Праверыць",
"maintenance_integrity_check_all": "Праверыць усе",
"maintenance_integrity_checksum_mismatch": "Несупадзенне кантрольнай сумы",
"maintenance_integrity_checksum_mismatch_description": "Файлы, кантрольная сума якіх на дыску не супадае з кантрольнай сумай, якую Immich захоўвае ў сваёй базе даных.",
"maintenance_integrity_checksum_mismatch_job": "Праверка на несупадзенне кантрольных сум",
"maintenance_integrity_checksum_mismatch_refresh_job": "Абнавіць справаздачы аб несупадзенні кантрольных сум",
"maintenance_integrity_missing_file": "Адсутныя файлы",
"maintenance_integrity_missing_file_description": "Файлы, якія ёсць у базе даных Immich, але не існуюць у файлавай сістэме.",
"maintenance_integrity_missing_file_job": "Праверка наяўнасці адсутных файлаў",
"maintenance_integrity_missing_file_refresh_job": "Абнавіць справаздачы аб адсутных файлах",
"maintenance_restore_backup": "Аднавіць рэзервовую копію",
+5
View File
@@ -189,18 +189,23 @@
"machine_learning_smart_search_enabled": "Включване на Интелигентно Търсене",
"machine_learning_smart_search_enabled_description": "Ако е деактивирано, изображенията няма да бъдат кодирани за Интелигентно Търсене.",
"machine_learning_url_description": "URL на сървъра за машинно обучение. Ако са предоставени повече от един URL, всеки сървър ще бъде опитан един по един, докато един отговори успешно, в реда от първия до последния. Сървъри, които не отговорят, ще бъдат временно игнорирани, докато не се върнат онлайн.",
"maintenance_backup_management": "Управление на архивирането",
"maintenance_delete_backup": "Изтриване на архив",
"maintenance_delete_backup_description": "Този файл ще бъде безвъзвратно изтрит.",
"maintenance_delete_error": "Неуспешно изтриване на архив.",
"maintenance_integrity_check": "Проверка",
"maintenance_integrity_check_all": "Провери всички",
"maintenance_integrity_checksum_mismatch": "Несъответствие на контролната сума",
"maintenance_integrity_checksum_mismatch_description": "Файлове, чиято контролна сума не съвпада със запазената в базата данни на Immich.",
"maintenance_integrity_checksum_mismatch_job": "Проверка на контролните суми",
"maintenance_integrity_checksum_mismatch_refresh_job": "Обнови докладите за проверка на конторлните суми",
"maintenance_integrity_missing_file": "Липсващи файлове",
"maintenance_integrity_missing_file_description": "Файлове, които Immich следи в базата си данни, но не са налични във файловата система.",
"maintenance_integrity_missing_file_job": "Проверка за липсващи файлове",
"maintenance_integrity_missing_file_refresh_job": "Обнови докладите за липсващи файлове",
"maintenance_integrity_report": "Отчет за непокътнатост",
"maintenance_integrity_untracked_file": "Непроследени файлове",
"maintenance_integrity_untracked_file_description": "Файлове в папките на Immich, за които Immich няма никакви записи.",
"maintenance_integrity_untracked_file_job": "Проверка за непроследени файлове",
"maintenance_integrity_untracked_file_refresh_job": "Обнови докладите за непроследени файлове",
"maintenance_restore_backup": "Възстановяване на архив",
+5
View File
@@ -189,18 +189,23 @@
"machine_learning_smart_search_enabled": "Povolit chytré vyhledávání",
"machine_learning_smart_search_enabled_description": "Pokud je vypnuto, obrázky nebudou kódovány pro inteligentní vyhledávání.",
"machine_learning_url_description": "URL serveru strojového učení. Pokud je zadáno více URL adres, budou jednotlivé servery zkoušeny postupně, dokud jeden z nich neodpoví úspěšně, a to v pořadí od prvního k poslednímu. Servery, které neodpoví, budou dočasně ignorovány, dokud nebudou opět online.",
"maintenance_backup_management": "Správa záloh",
"maintenance_delete_backup": "Smazat zálohu",
"maintenance_delete_backup_description": "Tento soubor bude trvale smazán.",
"maintenance_delete_error": "Nepodařilo se smazat zálohu.",
"maintenance_integrity_check": "Zkontrolovat",
"maintenance_integrity_check_all": "Zkontrolovat vše",
"maintenance_integrity_checksum_mismatch": "Neshoda kontrolního součtu",
"maintenance_integrity_checksum_mismatch_description": "Soubory, u nichž se kontrolní součet na disku neshoduje s kontrolním součtem, který Immich uložil do své databáze.",
"maintenance_integrity_checksum_mismatch_job": "Kontrola shod kontrolních součtů",
"maintenance_integrity_checksum_mismatch_refresh_job": "Obnovit hlášení o neshodách kontrolních součtů",
"maintenance_integrity_missing_file": "Chybějící soubory",
"maintenance_integrity_missing_file_description": "Soubory, které Immich sleduje ve své databázi, ale které neexistují v souborovém systému.",
"maintenance_integrity_missing_file_job": "Kontrola chybějících souborů",
"maintenance_integrity_missing_file_refresh_job": "Obnovit hlášení o chybějících souborech",
"maintenance_integrity_report": "Hlášení o integritě",
"maintenance_integrity_untracked_file": "Nesledované soubory",
"maintenance_integrity_untracked_file_description": "Soubory v adresářích služby Immich, o nichž nemá Immich žádný záznam.",
"maintenance_integrity_untracked_file_job": "Kontrola nesledovaných souborů",
"maintenance_integrity_untracked_file_refresh_job": "Obnovit hlášení o nesledovaných souborech",
"maintenance_restore_backup": "Obnovit zálohu",
+8 -3
View File
@@ -63,7 +63,7 @@
"backup_onboarding_footer": "Weitere Informationen zum Sichern von Immich findest du in der <link>Dokumentation</link>.",
"backup_onboarding_parts_title": "Eine 3-2-1-Sicherung umfasst:",
"backup_onboarding_title": "Sicherungen",
"backup_settings": "Einstellungen r Datenbanksicherung",
"backup_settings": "Einstellungen zur Datenbanksicherung",
"backup_settings_description": "Einstellungen zur regelmäßigen Sicherung der Datenbank. Hinweis: Diese Jobs werden nicht überwacht und du wirst nicht über Fehler informiert.",
"cleared_jobs": "Folgende Aufgaben zurückgesetzt: {job}",
"config_set_by_file": "Ist derzeit in einer Konfigurationsdatei festgelegt",
@@ -189,19 +189,24 @@
"machine_learning_smart_search_enabled": "Intelligente Suche aktivieren",
"machine_learning_smart_search_enabled_description": "Ist diese Option deaktiviert, werden die Bilder nicht für die intelligente Suche verwendet.",
"machine_learning_url_description": "Die URL des Servers für maschinelles Lernen. Wenn mehr als eine URL angegeben wird, wird jeder Server einzeln ausprobiert, bis einer erfolgreich antwortet, und zwar in der Reihenfolge vom ersten bis zum letzten. Server die nicht antworten werden temporär ignoriert, bis sie wieder verfügbar sind.",
"maintenance_backup_management": "Backup Management",
"maintenance_delete_backup": "Backup löschen",
"maintenance_delete_backup_description": "Diese Datei wird irreversibel gelöscht.",
"maintenance_delete_error": "Die Löschung der Sicherungskopie ist fehlgeschlagen.",
"maintenance_integrity_check": "Überprüfe",
"maintenance_integrity_check_all": "Überprüfe alle",
"maintenance_integrity_checksum_mismatch": "Prüfsummenfehler",
"maintenance_integrity_checksum_mismatch_job": "Auf Prüfsummen-Nichtübereinstimmungen prüfen",
"maintenance_integrity_checksum_mismatch_description": "Dateien, deren Prüfsumme auf dem Datenträger nicht mit der übereinstimmt, die Immich in seiner Datenbank gespeichert hat.",
"maintenance_integrity_checksum_mismatch_job": "Auf Prüfsummenfehler überprüfen",
"maintenance_integrity_checksum_mismatch_refresh_job": "Aktualisieren Sie Berichte über Prüfsummenkonflikte",
"maintenance_integrity_missing_file": "Fehlende Dateien",
"maintenance_integrity_missing_file_description": "Dateien, die Immich in seiner Datenbank erfasst hat, die aber im Dateisystem nicht vorhanden sind.",
"maintenance_integrity_missing_file_job": "Auf fehlende Dateien prüfen",
"maintenance_integrity_missing_file_refresh_job": "Berichte über fehlende Dateien aktualisieren",
"maintenance_integrity_report": "Integritätsbericht",
"maintenance_integrity_untracked_file": "Nicht getrackte Dateien",
"maintenance_integrity_untracked_file_job": "Überprüfen Sie ungetrackte Dateien",
"maintenance_integrity_untracked_file_description": "Dateien in Immichs Verzeichnissen, über die Immich keine Aufzeichnungen hat.",
"maintenance_integrity_untracked_file_job": "Auf nicht verfolgte Dateien prüfen",
"maintenance_integrity_untracked_file_refresh_job": "Berichte über nicht getrackte Dateien aktualisieren",
"maintenance_restore_backup": "Sicherungskopie wiederherstellen",
"maintenance_restore_backup_description": "Immich wird zurückgesetzt und von der ausgewählten Sicherungskopie wiederhergestellt. Ein Backup wird erstellt, bevor es weitergeht.",
+10 -2
View File
@@ -79,6 +79,7 @@
"cron_expression_description": "Agordu la intervalon de analizado pere de la formato de cron. Por pli da informoj, legu ekzemple <link>Crontab Guru</link>",
"cron_expression_presets": "Antaŭagordoj pri la cron-esprimo",
"disable_login": "Malebligi ensalutadon",
"download_csv": "Elŝuti .csv-dosieron",
"duplicate_detection_job_description": "Komenci permaŝin-lernadon por trovi similajn bildojn. Uzas 'inteligentan serĉadon'",
"exclusion_pattern_description": "Per skemo de ekskludo, vi povas ignori dosierojn kaj dosierujojn dum analizado de la biblioteko. Tio estas utila se vi havas ekz. RAW-dosierojn, kiujn vi ne volas importi.",
"export_config_as_json_description": "Elŝuti la aktualan sistem-agordaĵaron kiel JSON-dosieron",
@@ -188,9 +189,16 @@
"machine_learning_smart_search_enabled": "Ŝalti inteligentan serĉadon",
"machine_learning_smart_search_enabled_description": "Se malŝaltita, tiam bildoj ne estos kodigitaj por inteligenta serĉado.",
"machine_learning_url_description": "La URL-o de la maŝin-lerna servilo. Se vi donas pli ol unu URL-o, la sistemo provos ĉiun servilon unu post la alia ĝis kiam unu sukcese respondas, de la unua ĝis la lasta. Serviloj, kiuj ne respondas, estos dumtempe ignoritaj.",
"maintenance_backup_management": "Administrado de savkopioj",
"maintenance_delete_backup": "Forigi savkopion",
"maintenance_delete_backup_description": "La dosiero estos por ĉiam forigita.",
"maintenance_delete_error": "Malsukcesis forigi savkopion.",
"maintenance_integrity_check": "Kontroli",
"maintenance_integrity_check_all": "Kontroli ĉiujn",
"maintenance_integrity_checksum_mismatch": "Nekongruaj kontrolsumoj",
"maintenance_integrity_checksum_mismatch_description": "Dosieroj pri kiuj la kontrolsumo stokita sur disko ne kongruas kun tiu en la datumbazo de Immich.",
"maintenance_integrity_checksum_mismatch_job": "Kontroli pri nekongruaj kontrolsumoj",
"maintenance_integrity_checksum_mismatch_refresh_job": "Refreŝigi la raporton pri nekongruaj kontrolsumoj",
"maintenance_restore_backup": "Restaŭri savkopion",
"maintenance_restore_backup_description": "Immich estos forigita kaj reinstalita de la elektita savkopio. Nova savkopio estos kreita antaŭe.",
"maintenance_restore_backup_different_version": "Tiu ĉi savkopio estis kreita per alia versio de Immich!",
@@ -1664,14 +1672,14 @@
"no_explore_results_message": "Alŝutu pli da fotoj por esplori vian kolekton.",
"no_favorites_message": "Aldoni al Preferataĵoj por rapide retrovi viajn plej bonajn bildojn kaj videojn",
"no_libraries_message": "Krei eksteran biblitekon por vidi viajn fotojn kaj videojn",
"no_local_assets_found": "Neniuj lokaj elementoj trovitaj kun tiu kontrolsumo",
"no_local_assets_found": "Neniu loka elemento trovita kun tiu kontrolsumo",
"no_location_set": "Neniu loko agordita",
"no_locked_photos_message": "Fotoj kaj videoj en la ŝlosita dosierujo estas kaŝitaj, kaj ne montriĝos dum vi foliumas aŭ serĉas en via biblioteko.",
"no_name": "Neniu nomo",
"no_notifications": "Neniuj sciigoj",
"no_people_found": "Neniuj kongruaj homoj trovitaj",
"no_places": "Neniuj lokoj",
"no_remote_assets_found": "Neniuj foraj elementoj trovitaj kun tiu kontrolsumo",
"no_remote_assets_found": "Neniu fora elemento trovita kun tiu kontrolsumo",
"no_results": "Neniuj rezultoj",
"no_results_description": "Provu sinonimon aŭ pli ĝeneralan ŝlosilvorton",
"no_shared_albums_message": "Krei albumon por dividi fotojn kaj videojn kun konatoj",
+2 -2
View File
@@ -1500,7 +1500,7 @@
"logout_this_device_confirmation": "¿Estás seguro de que quieres cerrar sesión en este dispositivo?",
"logs": "Registros",
"longitude": "Longitud",
"look": "Mirar",
"look": "Aspecto",
"loop_videos": "Vídeos en bucle",
"loop_videos_description": "Habilite la reproducción automática de un video en el visor de detalles.",
"main_branch_warning": "Está utilizando una versión de desarrollo; ¡le recomendamos encarecidamente que utilice una versión de lanzamiento!",
@@ -2075,7 +2075,7 @@
"search_places": "Buscar lugar",
"search_rating": "Buscar por calificación...",
"search_result_page_new_search_hint": "Nueva Búsqueda",
"search_settings": "Ajustes de búsqueda",
"search_settings": "Buscar ajustes",
"search_state": "Buscar región/estado...",
"search_suggestion_list_smart_search_hint_1": "La búsqueda inteligente está habilitada por defecto, para buscar metadatos utiliza esta sintaxis ",
"search_suggestion_list_smart_search_hint_2": "m:tu-término-de-búsqueda",
+458 -2
View File
@@ -79,6 +79,7 @@
"cron_expression_description": "Ezarri eskaneatzeko tartea cron formatua erabiliz. Informazio gehiago lortzeko, jo mesedez <link>Crontab Guru</link> adibidera",
"cron_expression_presets": "Cron adierazpenaren aurrezarpenak",
"disable_login": "Desgaitu saio hastea",
"download_csv": "CSVa deskargatu",
"duplicate_detection_job_description": "Exekutatu ikasketa automatikoa aktiboetan antzeko irudiak detektatzeko. Bilaketa Adimendunean oinarritzen da",
"exclusion_pattern_description": "Bazterketa patroiek fitxategiak eta karpetak alde batera uzteko aukera ematen dizute liburutegia eskaneatzean. Oso erabilgarria da inportatu nahi ez dituzun fitxategiak dituzten karpetak badituzu, adibidez RAW fitxategiak.",
"export_config_as_json_description": "Deskargatu momentuko sistema konfigurazioa JSON fitxategi moduan",
@@ -87,12 +88,23 @@
"face_detection_description": "Hauteman aurpegiak elementuetan ikasketa automatikoa erabiliz. Bideoen kasuan, miniatura baino ez da kontuan hartuko. \"Freskatu\" aukerak elementu guztiak (bir-)prozesatzen ditu. \"Berrezarri\" aukerak horrez gain, uneko aurpegi-datu guztiak ezabatzen ditu. \"Falta direnak\" aukerak oraindik prozesatu ez diren elementuak jartzen ditu ilaran. Detektatutako aurpegiak Aurpegiaren Ezagutzarako ilaran jarriko dira Aurpegiaren Detekzioa amaitu ondoren, lehendik dauden edo berriak diren pertsonetan multzokatuz.",
"facial_recognition_job_description": "Detektatutako aurpegiak pertsonetan multzokatu. Urrats hau Aurpegi Detekzioaren ondoren egikaritzen da. \"Berrezarri\" aukerak aurpegi guztiak (bir-)taldekatzen ditu. \"Faltan\" aukerak izendatu gabeko aurpegiak ilaran jartzen ditu.",
"failed_job_command": "{command} komandoak hutsegin du {job} zereginerako",
"force_delete_user_warning": "ADI: Honek erabiltzailea eta baliabide guztiak ezabatuko lituzke. Akzio hau ezin da atzera bota eta fitxategiak ezin izango dira berreskuratu.",
"image_format": "Formatua",
"image_format_description": "WebP ereduak JPEG baino fitxategi txikiagoak sortzen ditu, baina motelagoa da kodifikatzen.",
"image_fullsize_description": "Tamaina osoko irudia metadaturik gabe, zoom egiterakoan erabiltzen da",
"image_fullsize_enabled": "Tamaina osoko irudien sorrera ezarri",
"image_fullsize_enabled_description": "Sortu tamaina osoko irudia web-erako egokiak ez diren formatuetan. \"Nahiago aurrebista txertatua\" gaituta dagoenean, aurrebista txertatuak zuzenean erabiltzen dira, bihurtu gabe. Ez die eragiten web-erako egokiak diren formatuei (JPEG, adibidez).",
"image_fullsize_quality_description": "Tamaina osoko irudiaren kalitatea (1-100). Zenbat eta altuagoa, orduan eta hobea, baina fitxategi handiagoak sortzen ditu.",
"image_fullsize_title": "Tamaina osoko irudien ezarpenak",
"image_prefer_embedded_preview": "Nahiago aurrebista txertatua",
"image_prefer_embedded_preview_setting_description": "Erabili RAW argazkien aurrebista txertatuak irudiak prozesatzeko abiapuntu gisa (eskuragarri daudenean). Honek kolore zehatzagoak eman ditzake irudi batzuetan, baina aurrebistaren kalitatea kameraren araberakoa da eta irudiak konpresio-artefaktu gehiago izan ditzake.",
"image_prefer_wide_gamut": "Nahiago gamut zabala",
"image_prefer_wide_gamut_setting_description": "Erabili Display P3 miniaturentzako (thumbnails). Honek hobeto mantentzen du kolore-espazio zabaleko irudien bizitasuna, baina irudiak ezberdin ikus daitezke arakatzaile bertsio zaharra duten gailu zaharkituetan. sRGB irudiak sRGB gisa mantentzen dira kolore-aldaketak saihesteko.",
"image_preview_description": "Tamaina baxuko irudia metadaturik gabe, baliabide bakarra bistaratzerakoan eta ikasketa automatikoan erabiltzeko",
"image_preview_quality_description": "Aurrebisten kalitatea (1-100). Zenbat eta altuagoa, orduan eta hobea, baina fitxategi handiagoak sortzen ditu eta aplikazioaren jarioa moteldu dezake. Balio baxu bat ezartzeak ikasketa automatikoaren kalitatea kaltetu dezake.",
"image_preview_title": "Aurreikusiaen Konfigurazioa",
"image_progressive": "Progresiboa",
"image_progressive_description": "Kodetu JPEG irudiak progresiboki, pixkanaka kargatzen joan daitezen. Honek ez du eraginik WebP irudietan.",
"image_quality": "Kalitatea",
"image_resolution": "Erresoluzioa",
"image_settings": "Argazkien Konfigurazioa",
@@ -136,26 +148,470 @@
"oauth_storage_label_claim": "Memoriaren etiketa eskaera",
"oauth_storage_label_claim_description": "Erabiltzailearen memoria-etiketa automatikoki finkatzea, eskatutako balioan.",
"oauth_storage_quota_claim": "Eskatutako memoriaren kuota",
"transcoding_acceleration_vaapi": "VAAPI"
"template_email_preview": "Aurrebista",
"transcoding_acceleration_vaapi": "VAAPI",
"transcoding_threads": "Hariak",
"transcoding_tone_mapping": "Tonoen mapeoa"
},
"advanced": "Aurreratua",
"advanced_settings_readonly_mode_title": "Irakurri-soilik modua",
"advanced_settings_troubleshooting_title": "Arazoak detektatzea eta konpontzea",
"album_info_card_backup_album_excluded": "BAZTERTUTAKOAK",
"album_info_card_backup_album_included": "BARNEKOAK",
"album_viewer_appbar_share_leave": "Albuma utzi",
"album_viewer_appbar_share_to": "Albuma partekatu",
"album_viewer_page_share_add_users": "Erabiltzaileak gehitu",
"anti_clockwise": "Erloju-orratzen noranzkoaren aurka",
"app_bar_signout_dialog_ok": "Bai",
"app_bar_signout_dialog_title": "Saioa itxi",
"apply_count": "Ezarri ({count, number})",
"archive": "Artxibo",
"archive_page_title": "Artxibo ({count})",
"archived": "Artxibatua",
"asset_hashing": "Hasha kalkulatzen…",
"asset_list_group_by_sub_title": "Multzokatu",
"asset_list_layout_settings_dynamic_layout_title": "Diseinu dinamikoa",
"asset_list_layout_settings_group_automatically": "Automatikoa",
"asset_list_layout_sub_title": "Antolaketa",
"asset_list_settings_title": "Irudi lauki-sarea",
"asset_skipped": "Alde batera utzita",
"asset_uploaded": "Igota",
"asset_uploading": "Igotzen…",
"asset_viewer_settings_title": "Baliabide ikuslea",
"assets": "Baliabideak",
"assets_added_to_albums_count": "Gehituta {assetTotal, plural, one {# asset} other {# assets}} to {albumTotal, plural, one {# album} other {# albums}}",
"assets_cannot_be_added_to_albums": "{count, plural, one {Asset} other {Assets}} ezin izan da albumetara gehitu",
"assets_were_part_of_albums_count": "{count, plural, one {Asset was} other {Assets were}} dagoeneko albumean dago",
"back": "Atzera",
"backup": "Babes-kopia",
"backup_album_selection_page_select_albums": "Albumak aukeratu",
"backup_album_selection_page_selection_info": "Aukeraren informazioa",
"backup_all": "Denak",
"backup_background_service_current_upload_notification": "{filename} igotzen",
"backup_background_service_error_title": "Akatsa babes-kopia egiterakoan",
"backup_controller_page_albums": "Seguratsun-kopia albumak",
"backup_controller_page_background_battery_info_ok": "Onartu",
"backup_controller_page_background_battery_info_title": "Bateria optimizazioak",
"backup_controller_page_backup": "Babes-kopia",
"backup_controller_page_backup_selected": "Aukeratutakoa: ",
"backup_controller_page_excluded": "Baztertutakoa: ",
"backup_controller_page_failed": "Akatsak ({count})",
"backup_controller_page_id": "ID: {id}",
"backup_controller_page_info": "Segurtasun-kopiaren informazioa",
"backup_controller_page_none_selected": "Aukerarik ez",
"backup_controller_page_remainder": "Gainerakoak",
"backup_controller_page_server_storage": "Zerbitzariko memoria",
"backup_controller_page_start_backup": "Segurtasun-kopia hasi",
"backup_info_card_assets": "baliabide",
"backup_manual_cancelled": "Ezeztatuta",
"backup_manual_success": "Arrakastatsua",
"backup_manual_title": "Igoera egoera",
"backup_options_page_title": "Babes-kopia ezarpenak",
"backward": "Atzeruntz",
"build": "Bertsioa",
"cache_settings_clear_cache_button": "Cache-memoria garbitu",
"cache_settings_duplicated_assets_clear_button": "GARBITU",
"cache_settings_statistics_album": "Liburutegiaren miniaturak",
"cache_settings_statistics_full": "Tamainu osoko irudiak",
"cache_settings_statistics_thumbnail": "Miniatura",
"cache_settings_statistics_title": "Cache-memoria erabilera",
"cache_settings_tile_title": "Memoria lokala",
"cache_settings_title": "Cache-memoria ezarpenak",
"camera": "Kamera",
"cancel": "Ezeztatu",
"canceled": "Ezeztatua",
"canceling": "Desgaitzen",
"cast": "Transmititu",
"change_password_form_confirm_password": "Pasahitza baieztu",
"change_password_form_new_password": "Pasahitza berria",
"check_corrupt_asset_backup_button": "Egiaztapena burutu",
"city": "Hiria",
"clear": "Garbitu",
"client_cert_dialog_msg_confirm": "Ok",
"client_cert_enter_password": "Pasahitza sartu",
"client_cert_import": "Inportatu",
"clockwise": "Erloju-orratzen noranzkoan",
"close": "Itxi",
"collapse": "Taldekatu",
"color": "Kolorea",
"completed": "Burututa",
"confirm": "Onartu",
"contain": "Egokitu",
"context": "Kontextua",
"continue": "Jarraitu",
"control_bottom_app_bar_edit_location": "Kokapena eguneratu",
"control_bottom_app_bar_share_link": "Esteka partekatu",
"control_bottom_app_bar_share_to": "Partekatu",
"country": "Herrialdea",
"cover": "Portada",
"covers": "Portada",
"create": "Sortu",
"create_album_page_untitled": "Izengabea",
"create_new": "BERRIA SORTU",
"create_shared_album_page_share_add_assets": "BALIABIDEAK GEHITU",
"create_shared_album_page_share_select_photos": "Argazkiak aukeratu",
"created": "Sortuta",
"created_at": "Sortze-data",
"crop": "Ebaki",
"curated_object_page_title": "Objektuak",
"dark": "Iluna",
"day": "Eguna",
"days": "Egunak",
"delete": "Ezabatu",
"delete_dialog_ok_force": "Hala ere ezabatu",
"delete_dialog_title": "Behin betiko ezabatu",
"delete_local_dialog_ok_force": "Hala ere ezabatu",
"description": "Deskribapena",
"description_input_hint_text": "Deskribapena ezarri…",
"details": "Xehetasunak",
"direction": "Norabidea",
"disabled": "Desgaituta",
"discord": "Discord",
"discover": "Aurkitu",
"documentation": "Dokumentazioa",
"done": "Amaitu",
"download": "Deskargatu",
"download_canceled": "Deskarga ezeztatuta",
"download_complete": "Deskarga burututa",
"download_enqueue": "Deskarga ilaran",
"download_error": "Akatsa deskargatzerakoan",
"download_failed": "Akatsa deskargatzerakoan",
"download_finished": "Deskarga burututa",
"download_paused": "Deskarga geldituta",
"download_settings": "Deskargak",
"download_started": "Deskarga hasieratua",
"download_sucess": "Deskarga arrakastatsua",
"downloading": "Deskargatzen",
"downloading_media": "Baliabideak deskargatzen",
"duplicates": "Kopiak",
"duration": "Iraupena",
"edit": "Editatu",
"edit_location_dialog_title": "Kokapena",
"editor": "Editorea",
"email": "E-mail",
"enable": "Gaitu",
"enabled": "Gaituta",
"enqueued": "Ilaran gehituta",
"error": "Akatsa",
"error_saving_image": "Akatsa: {error}",
"exif": "Exif",
"exif_bottom_sheet_description": "Deskribapena ezarri…",
"exif_bottom_sheet_details": "XEHETASUNAK",
"exif_bottom_sheet_location": "KOKAPENA",
"exif_bottom_sheet_people": "PERTSONAK",
"exif_bottom_sheet_person_add_person": "Izena gehitu",
"experimental_settings_title": "Esperimental",
"expired": "Iraungita",
"explore": "Bilatu",
"explorer": "Bilatzailea",
"export": "Esportatu",
"extension": "Hedapena",
"external": "Kanpokoa",
"external_network": "Kanpoko sarea",
"face_unassigned": "Ezarri gabea",
"failed": "Akatsduna",
"favorite": "Gogokoa",
"favorites": "Gogokoenak",
"features": "Ezaugarriak",
"filename": "Fitxategia",
"filetype": "Fitxategi mota",
"filter": "Iragazkia",
"first": "Lehenengo «Lehenik»",
"folder": "Karpeta",
"folders": "Karpetak",
"forward": "Aurreruntz",
"general": "General",
"gps": "GPS",
"gps_missing": "Ez dago GPS",
"grant_permission": "Baimendu",
"haptic_feedback_title": "Ukipen-feedbacka",
"hashing": "Hasha sortzen",
"header_settings_add_header_tip": "Goiburua gehitu",
"header_settings_header_name_input": "Goiburu izena",
"header_settings_header_value_input": "Goiburu balioa",
"host": "Host",
"hour": "Ordua",
"hours": "Orduak",
"id": "ID",
"idle": "Jarduerarik gabe",
"image": "Irudia",
"image_saved_successfully": "Irudia gordeta",
"image_viewer_page_state_provider_download_started": "Deskarga hasieratua",
"image_viewer_page_state_provider_download_success": "Deskarga arrakastatsua",
"info": "Informazioa",
"jobs": "Atazak",
"keep": "Mantendu",
"language": "Hizkuntza",
"last": "Azkena",
"latitude": "Latitudea",
"leave": "Bertan behera utzi",
"level": "Maila",
"library": "Liburutegia",
"licenses": "Lizentziak",
"light": "Argia",
"like": "Gustoko",
"list": "Zerrenda",
"loading": "Kargatzen",
"local": "Lokal",
"lock": "Blokeatu",
"login": "Saioa hasi",
"login_form_back_button_text": "Atzera",
"login_form_email_hint": "zure@emaila.com",
"login_form_endpoint_hint": "http://zerbitzariaren-ip-a:portua",
"login_form_password_hint": "pasahitza",
"logs": "Erregistroak",
"longitude": "Longitudea",
"look": "Itxura",
"main_menu": "Menu nagusia",
"make": "Marka",
"manage_geolocation": "Kudeatu kokapena",
"map": "Mapa",
"map_location_dialog_yes": "Bai",
"matches": "Bat etorritakoak",
"memories": "Gogorapenak",
"memory": "Gogorapena",
"menu": "Menua",
"merge": "Batu",
"minimize": "Txikitu",
"minute": "Minutua",
"minutes": "Minutuak",
"missing": "Hutsegiteak",
"model": "Modeloa",
"month": "Hilabetea",
"more": "Gehiago",
"move": "Mugitu",
"name": "Izena",
"networking_settings": "Sarea",
"never": "Inoiz ez",
"next": "Hurrengoa",
"no": "Ez",
"notes": "Oharra",
"notifications": "Jakinarazpenak",
"oauth": "OAuth",
"offline": "Lineaz kanpo",
"offset": "Desbiderapena",
"ok": "Bai",
"onboarding": "Ezartzen",
"online": "Linean",
"open": "Zabalik",
"options": "Ezarpenak",
"or": "edo",
"organize_into_albums": "Albumetan antolatu",
"original": "Originala",
"other": "Beste batzuk",
"owned": "Berezkoak",
"owner": "Jabea",
"partner": "Kidea",
"partners": "Kideak",
"password": "Pasahitza",
"path": "Bidea",
"pattern": "Patroia",
"pause": "Gelditu",
"paused": "Geldituta",
"pending": "Itxarotzen",
"people": "Pertsonak",
"permission": "Baimena",
"permission_onboarding_back": "Atzera",
"person": "Pertsona",
"photos": "Argazkiak",
"place": "Tokia",
"places": "Tokiak",
"play": "Erreproduzitu",
"port": "Portua",
"preferences_settings_title": "Ezarpenak",
"preset": "Txantiloia",
"preview": "Aurrebista",
"previous": "Aurrekoa",
"primary": "Nagusia",
"privacy": "Pribatutasuna",
"profile": "Profila",
"profile_drawer_app_logs": "Erregistroak",
"profile_drawer_github": "GitHub",
"purchase_account_info": "Laguntzaile",
"purchase_button_activate": "Aktibatu",
"purchase_button_buy": "Erosi",
"purchase_button_select": "Aukeratu",
"purchase_individual_title": "Banakakoa",
"purchase_server_title": "Zerbitzaria",
"query_asset_id": "Aztertu aukeratutako ID-a",
"readonly_mode_disabled": "Irakurri-bakarrik modua desgaituta",
"readonly_mode_enabled": "Irakurri-bakarrik modua gaituta",
"reassign": "Berrezarri",
"recent": "Berria",
"refresh": "Freskatu",
"refreshed": "Freskatuta",
"remote": "Urruneko zerbitzaria",
"remove": "Kendu",
"rename": "Izena aldatu",
"repair": "Konpondu",
"repository": "Errepositorioa",
"rescan": "Berreskaneatu",
"reset": "Berrasieratu",
"restore": "Zaharberritu",
"resume": "Jarraitu",
"role": "Rola",
"role_editor": "Editorea",
"role_viewer": "Ikuslea",
"running": "Martxan",
"save": "Gorde",
"scan_library": "Eskaneatu",
"search": "Bilatu",
"search_filter_date": "Data",
"search_filter_location": "Kokapena",
"search_for": "Bilatu",
"search_no_people": "Pertsonarik ez",
"search_options": "Bilaketa ezarpenak",
"search_page_categories": "Kategoriak",
"search_page_screenshots": "Pantaila-argazkiak",
"search_page_selfies": "Selfiak",
"search_page_things": "Gauzak eta Animaliak",
"search_people": "Pertsonak bilatu",
"search_places": "Tokiak bilatu",
"search_settings": "Ezarpenak bilatu",
"search_state": "Probintziaren arabera bilatu…",
"search_suggestion_list_smart_search_hint_2": "m:zure-bilaketa",
"search_tags": "Etiketak bilatu…",
"search_timezone": "Ordu-eremua bilatu…",
"search_type": "Bilaketa mota",
"searching_locales": "Tokiak bilatzen…",
"second": "Segundua",
"select": "Aukeratu",
"select_all": "Dena aukeratu",
"select_face": "Aurpegia hautatu",
"select_photos": "Argazkiak aukeratu",
"selected": "Aukeratuta",
"selected_gps_coordinates": "GPS Koordenadak Aukeratuta",
"send_message": "Mezua bidali",
"server_offline": "Zerbitzaria lineaz kanpo",
"server_online": "Zerbitzaria linean",
"server_stats": "Zerbitzariaren estatistikak",
"server_version": "Zerbitzariaren bertsioa",
"set": "Ezarri",
"setting_image_viewer_title": "Irudiak",
"setting_languages_apply": "Ezarri",
"setting_notifications_notify_immediately": "berehala",
"setting_notifications_notify_never": "inoiz ez",
"setting_video_viewer_looping_title": "Errepikatu",
"settings": "Ezarpenak",
"settings_saved": "Ezarpenak gordeta",
"share": "Partekatu",
"share_dialog_preparing": "Prestatzen...",
"shared": "Partekatuta",
"shared_album_section_people_title": "PERTSONAK",
"shared_by": "Honek partekatuta",
"shared_link_info_chip_metadata": "EXIF",
"shared_links": "Partekatutako estekak",
"sharing": "Partekatzen",
"show_albums": "Albumak erakutsi",
"show_gallery": "Galeria erakutsi",
"show_metadata": "Metadatuak erakutsi",
"show_password": "Pasahitza erakutsi",
"show_supporter_badge": "Kolaboratzaile entseina",
"shuffle": "Nahasi",
"sidebar": "Alboko panela",
"sign_out": "Saioa itxi",
"sign_up": "Erregistratu",
"size": "Tamaina",
"slideshow": "Diapositibak",
"slideshow_settings": "Diapositiba ezarpenak",
"sort_created": "Sortze data",
"sort_modified": "Egunatze data",
"sort_newest": "Argazkirik berriena",
"sort_oldest": "Argazkirik zaharrena",
"sort_title": "Izenburua",
"source": "Iturburua",
"stack": "Multzokatu",
"stack_duplicates": "Kopiak multzokatu",
"stacktrace": "Pila jarraipena",
"start": "Hasi",
"start_date": "Hasiera data",
"state": "Estatua / Probintzia",
"status": "Egoera",
"storage": "Memoria edukiera",
"storage_label": "Memoria etiketa",
"submit": "Bidali",
"success": "Arrakastatsua",
"suggestions": "Iradokizunak",
"support": "Babesa",
"sync": "Sinkronizazioa",
"tag": "Etiketa",
"tag_assets": "Baliabideak etiketatu",
"tag_people": "Pertsonak etiketatu",
"tags": "Etiketak",
"template": "Txantiloia",
"theme": "Gaia",
"theme_selection": "Gaien aukeraketa",
"third_party_resources": "Bitartekoen baliabideak",
"time_based_memories": "Denboran oinarritutako gogorapenak",
"timeline": "Kronologia",
"timezone": "Ordu-eremua",
"to_archive": "Artxibatu",
"to_change_password": "Pasahitza aldatu",
"to_favorite": "Gogokotzat ezarri",
"to_login": "Saioa hasi",
"to_select": "aukeratzeko",
"view_similar_photos": "Ikusi antzeko argazkiak"
"to_trash": "Baztertu",
"toggle_settings": "Ezarpenak aldizkatu",
"total": "Guztira",
"total_usage": "Erabilpen osoa",
"trash": "Zakarrontzia",
"trash_all": "Denak ezabatu",
"trash_delete_asset": "Baliabidea ezabatu/zakarrontzira eraman",
"troubleshoot": "Arazoak konpontzea",
"type": "Mota",
"unarchive": "Desartxibatu",
"undo": "Desegin",
"unfavorite": "Gogokoetatik kendu",
"unhide_person": "Pertsona erakutsi",
"unknown": "Ezezaguna",
"unknown_country": "Herrialde ezezaguna",
"unknown_year": "Urte ezezaguna",
"unlimited": "Mugagabea",
"unlink_oauth": "OAuth deslotu",
"unmute_memories": "Gogorapenen soinua gaitu",
"unnamed_album": "Izengabeko Albuma",
"unnamed_share": "Izengabeko baliabide partekatua",
"unsaved_change": "Gordegabeko aldaketa",
"unselect_all": "Aukeraketak garbitu",
"unstack": "Multzotik kendu",
"untagged": "Etiketagabea",
"up_next": "Hurrengoa",
"updated_at": "Eguneratze-data",
"updated_password": "Pasahitza eguneratuta",
"upload": "Igo",
"upload_concurrency": "Igoera paraleloak",
"upload_status_duplicates": "Kopiak",
"upload_status_errors": "Akatsak",
"upload_status_uploaded": "Igota",
"uploading": "Igotzen",
"url": "URL-a",
"usage": "Erabilera",
"user": "Erabiltzailea",
"user_id": "Erabiltzaile ID-a",
"user_purchase_settings": "Erosketa",
"username": "Erabiltzaile izena",
"users": "Erabiltzaileak",
"utilities": "Tresnak",
"validate": "Balioetsi",
"variables": "Aldagaiak",
"version": "Bertsioa",
"version_history": "Bertsio-historia",
"video": "Bideoa",
"videos": "Bideoak",
"view": "Bista",
"view_album": "Albuma ikusi",
"view_all": "Denak ikusi",
"view_link": "Esteka ikusi",
"view_links": "Estekak ikusi",
"view_name": "Bista",
"view_similar_photos": "Ikusi antzeko argazkiak",
"view_stack": "Pila ikusi",
"viewer_unstack": "Multzotik kendu",
"waiting": "Itxarotzen",
"warning": "Oharra",
"week": "Astea",
"welcome": "Ongi etorri",
"year": "Urtea",
"yes": "Bai",
"zoom_image": "Irudia handitu"
}
+14 -1
View File
@@ -189,18 +189,23 @@
"machine_learning_smart_search_enabled": "Activar busca intelixente",
"machine_learning_smart_search_enabled_description": "Se está desactivado, as imaxes non se codificarán para a busca intelixente.",
"machine_learning_url_description": "A URL do servidor de aprendizaxe automática. Se se proporciona máis dunha URL, intentarase con cada servidor un por un ata que un responda correctamente, en orde do primeiro ao último. Os servidores que non respondan ignoraranse temporalmente ata que volvan estar en liña.",
"maintenance_backup_management": "Xestión de copias de seguridade",
"maintenance_delete_backup": "Eliminar copia de seguridade",
"maintenance_delete_backup_description": "Este arquivo será borrado permanentemente.",
"maintenance_delete_error": "Erro ao eliminar a copia de seguridade.",
"maintenance_integrity_check": "Verificar",
"maintenance_integrity_check_all": "Seleccionar todos",
"maintenance_integrity_checksum_mismatch": "A suma de comprobación non coincide",
"maintenance_integrity_checksum_mismatch_description": "Arquivos que teñen diferentes sumas de comprobación entre o disco e a base de datos de Immich.",
"maintenance_integrity_checksum_mismatch_job": "Comproba se hai discrepancias na suma de verificación",
"maintenance_integrity_checksum_mismatch_refresh_job": "Actualizar informes de discrepancias da suma de comprobación",
"maintenance_integrity_missing_file": "Arquivos faltantes",
"maintenance_integrity_missing_file_description": "Arquivos que Immich ten rexistrados na base de datos pero que non existen no sistema de arquivos.",
"maintenance_integrity_missing_file_job": "Comprobe os arquivos que faltan",
"maintenance_integrity_missing_file_refresh_job": "Actualiza os informes de arquivos que faltan",
"maintenance_integrity_report": "Informe de integridade",
"maintenance_integrity_untracked_file": "Arquivos sen rastrexar",
"maintenance_integrity_untracked_file_description": "Arquivos nos directorios de Immich dos que Immich non ten ningún rexistro.",
"maintenance_integrity_untracked_file_job": "Comprobar se hai arquivos sen rastrexar",
"maintenance_integrity_untracked_file_refresh_job": "Actualizar informes de arquivos sen rastrexar",
"maintenance_restore_backup": "Recuperar copia de seguridade",
@@ -1369,6 +1374,7 @@
"individual_share": "Compartir individual",
"individual_shares": "Comparticións individuais",
"info": "Información",
"integrity_checks": "Verificacións de integridade",
"interval": {
"day_at_onepm": "Todos os días ás 13:00",
"hours": "Cada {hours, plural, one {hora} other {{hours, number} horas}}",
@@ -1441,6 +1447,7 @@
"linked_oauth_account": "Conta OAuth ligada",
"list": "Lista",
"live": "En directo",
"load_more": "Cargar máis",
"loading": "Cargando",
"loading_search_results_failed": "Erro ao cargar os resultados da busca",
"local": "Local",
@@ -2099,6 +2106,7 @@
"select_person": "Seleccionar persoa",
"select_person_to_tag": "Seleccionar unha persoa para etiquetar",
"select_photos": "Seleccionar fotos",
"select_quality": "Seleccionar calidade",
"select_trash_all": "Seleccionar mover todo ao lixo",
"select_user_for_sharing_page_err_album": "Erro ao crear o álbum",
"selected": "Seleccionado",
@@ -2162,6 +2170,8 @@
"share_assets_selected": "{count} seleccionados",
"share_dialog_preparing": "Preparando...",
"share_link": "Ligazón para Compartir",
"share_original": "Utilizar orixinal (grande)",
"share_preview": "Utilizar miniatura (pequena)",
"shared": "Compartido",
"shared_album_activities_input_disable": "O comentario está desactivado",
"shared_album_activity_remove_content": "Quere eliminar esta actividade?",
@@ -2263,6 +2273,7 @@
"slideshow_repeat_description": "Volver ao principio ao rematar a presentación de diapositivas",
"slideshow_settings": "Configuración da presentación",
"smart_album": "Álbume intelixente",
"some_assets_already_have_a_location_warning": "Algúns dos recursos seleccionados xa teñen unha localización",
"sort_albums_by": "Ordenar álbums por...",
"sort_created": "Data de creación",
"sort_items": "Número de elementos",
@@ -2383,11 +2394,13 @@
"trash_page_title": "Lixo ({count})",
"trashed_items_will_be_permanently_deleted_after": "Os elementos no lixo eliminaranse permanentemente despois de {days, plural, one {# día} other {# días}}.",
"trigger": "Disparador",
"trigger_asset_metadata_extraction": "Extracción de metadatos de activos",
"trigger_asset_metadata_extraction_description": "Activado cando se extraen os metadatos EXIF dun recurso",
"trigger_asset_uploaded": "Carga de activos",
"trigger_asset_uploaded_description": "Actívase cando se carga un activo novo",
"trigger_description": "Un evento que inicia o fluxo de traballo",
"trigger_person_recognized": "Persoa recoñecida",
"trigger_person_recognized_description": "Actívase cando se detecta a unha persoa",
"trigger_person_recognized_description": "Actívase cando se recoñece a unha persoa",
"trigger_type": "TIpo de disparador",
"troubleshoot": "Solucionar problemas",
"type": "Tipo",
+3
View File
@@ -196,13 +196,16 @@
"maintenance_integrity_check": "Ellenőrizze",
"maintenance_integrity_check_all": "Mind ellenőrzése",
"maintenance_integrity_checksum_mismatch": "Ellenőrzőösszeg-eltérés",
"maintenance_integrity_checksum_mismatch_description": "Azon fájlok, amelyek lemezen tárolt ellenőrzőösszege eltér az Immich adatbázisában szereplőtől.",
"maintenance_integrity_checksum_mismatch_job": "Az ellenőrzőösszegek eltéréseinek ellenőrzése",
"maintenance_integrity_checksum_mismatch_refresh_job": "Az ellenőrzőösszeg-eltérésről szóló jelentések frissítése",
"maintenance_integrity_missing_file": "Hiányzó fájlok",
"maintenance_integrity_missing_file_description": "Az Immich adatbázisában szereplő, de a fájlrendszerben nem létező fájlok.",
"maintenance_integrity_missing_file_job": "Hiányzó fájlok ellenőrzése",
"maintenance_integrity_missing_file_refresh_job": "Hiányzó fájl riport frissítése",
"maintenance_integrity_report": "Integritási jelentés",
"maintenance_integrity_untracked_file": "Nem figyelt/követett fájlok",
"maintenance_integrity_untracked_file_description": "Az Immich könyvtáraiban található, de a nyilvántartásában nem szereplő fájlok.",
"maintenance_integrity_untracked_file_job": "Nem követett fájlok ellenőrzése",
"maintenance_integrity_untracked_file_refresh_job": "Nem nyomon követett fájlok riportjának frissítése",
"maintenance_restore_backup": "Biztonsági mentés visszaállítása",
+5
View File
@@ -189,18 +189,23 @@
"machine_learning_smart_search_enabled": "Attiva ricerca intelligente",
"machine_learning_smart_search_enabled_description": "Se disabilitato le immagini non saranno codificate per la ricerca intelligente.",
"machine_learning_url_description": "URL del server machine learning. Se sono stati forniti più di un URL, verrà testato un server alla volta finché uno non risponderà, in ordine dal primo all'ultimo. I server che non rispondono saranno temporaneamente ignorati finché non torneranno online.",
"maintenance_backup_management": "Gestione backup",
"maintenance_delete_backup": "Elimina backup",
"maintenance_delete_backup_description": "Questo file sarà eliminato in modo irreversibile.",
"maintenance_delete_error": "Impossibile eliminare il backup.",
"maintenance_integrity_check": "Verifica",
"maintenance_integrity_check_all": "Verifica Tutto",
"maintenance_integrity_checksum_mismatch": "I checksum non corrispondono",
"maintenance_integrity_checksum_mismatch_description": "File il cui checksum sul disco non corrisponde al checksum memorizzato da Immich nel database.",
"maintenance_integrity_checksum_mismatch_job": "Verifica corrispondenza dei checksum",
"maintenance_integrity_checksum_mismatch_refresh_job": "Aggiorna report corrispondenze dei checksum",
"maintenance_integrity_missing_file": "File mancanti",
"maintenance_integrity_missing_file_description": "File che Immich ha registrato nel suo database ma che non esistono sul file system.",
"maintenance_integrity_missing_file_job": "Verifica file mancanti",
"maintenance_integrity_missing_file_refresh_job": "Aggiorna report sui file mancanti",
"maintenance_integrity_report": "Report di integrità",
"maintenance_integrity_untracked_file": "File non tracciati",
"maintenance_integrity_untracked_file_description": "File nelle directory di Immich di cui Immich non ha alcuna traccia.",
"maintenance_integrity_untracked_file_job": "Verifica presenza file non tracciati",
"maintenance_integrity_untracked_file_refresh_job": "Aggiorna il report dei file non tracciati",
"maintenance_restore_backup": "Ripristina backup",
+2
View File
@@ -189,9 +189,11 @@
"machine_learning_smart_search_enabled": "스마트 검색 활성화",
"machine_learning_smart_search_enabled_description": "비활성화하면 스마트 검색을 위한 이미지 처리를 진행하지 않습니다.",
"machine_learning_url_description": "기계 학습 서버의 URL을 설정합니다. 여러 개가 입력되면 첫 번째부터 한 번에 하나씩 순서대로 응답하는 서버를 찾을 때까지 요청을 시도합니다. 응답하지 않는 서버는 다시 사용 가능할 때까지 일시적으로 제외됩니다.",
"maintenance_backup_management": "백업 관리",
"maintenance_delete_backup": "백업 삭제",
"maintenance_delete_backup_description": "이 파일이 영구적으로 삭제됩니다.",
"maintenance_delete_error": "백업 삭제에 실패했습니다.",
"maintenance_integrity_check": "체크",
"maintenance_integrity_check_all": "전체선택",
"maintenance_integrity_checksum_mismatch": "체크섬 불일치",
"maintenance_integrity_checksum_mismatch_job": "파일 무결성 검사",
+5
View File
@@ -189,18 +189,23 @@
"machine_learning_smart_search_enabled": "Aktiver smart søk",
"machine_learning_smart_search_enabled_description": "Hvis deaktivert, vil bilder ikke bli enkodet for smart søk.",
"machine_learning_url_description": "URL til maskinlærings-serveren. Hvis mer enn en URL er lagt inn, hver server vill bli forsøkt en om gangen frem til en svarer suksessfullt, i rekkefølge fra først til sist. Servere som ikke svarer vil midlertidig bli oversett frem til dem svarer igjen.",
"maintenance_backup_management": "Administrasjon av sikkerhetskopier",
"maintenance_delete_backup": "Slett sikkerhetskopi",
"maintenance_delete_backup_description": "Denne filen vil bli permanent slettet.",
"maintenance_delete_error": "Feilet ved sletting av sikkerhetskopi.",
"maintenance_integrity_check": "Sjekk",
"maintenance_integrity_check_all": "Velg alle",
"maintenance_integrity_checksum_mismatch": "Sjekksum er feil",
"maintenance_integrity_checksum_mismatch_description": "Filer med en sjekksum på disken som ikke stemmer overens med sjekksummen Immich har lagret i databasen sin.",
"maintenance_integrity_checksum_mismatch_job": "Sjekk for feilede sjekksummer",
"maintenance_integrity_checksum_mismatch_refresh_job": "Oppdater feilede sjekksum rapporter",
"maintenance_integrity_missing_file": "Manglende filer",
"maintenance_integrity_missing_file_description": "Filer som Immich har sporet i databasen sin, men som ikke eksisterer i filsystemet.",
"maintenance_integrity_missing_file_job": "Sjekk etter manglende filer",
"maintenance_integrity_missing_file_refresh_job": "Oppdater rapporten for manglende filer",
"maintenance_integrity_report": "Integritetsrapport",
"maintenance_integrity_untracked_file": "Usporede filer",
"maintenance_integrity_untracked_file_description": "Filer i Immich-mappene som ikke er registrert i databasen.",
"maintenance_integrity_untracked_file_job": "Sjekk etter usporede filer",
"maintenance_integrity_untracked_file_refresh_job": "Oppdater rapporten for usporede filer",
"maintenance_restore_backup": "Gjenopprett Sikkerhetskopi",
+9 -4
View File
@@ -56,7 +56,7 @@
"backup_database": "Criar Cópia da Base de Dados",
"backup_database_enable_description": "Ativar cópias da base de dados",
"backup_keep_last_amount": "Quantidade de cópias anteriores a manter",
"backup_onboarding_1_description": "Uma cópia remota na cloud ou outra localização física.",
"backup_onboarding_1_description": "Uma cópia remota na cloud ou noutra localização física.",
"backup_onboarding_2_description": "Cópias locais em dispositivos diferentes, incluindo os ficheiros principais e uma cópia de segurança local dos mesmos.",
"backup_onboarding_3_description": "Cópias completas dos seus dados, incluindo os ficheiros originais. Inclui uma cópia remota e duas cópias locais.",
"backup_onboarding_description": "É recomendada a <backblaze-link>estratégia de cópia de segurança 3-2-1</backblaze-link> para proteger os seus dados. Para uma solução de cópia de segurança completa, deve manter cópias das suas fotos e vídeos tal como da base de dados do Immich.",
@@ -189,18 +189,23 @@
"machine_learning_smart_search_enabled": "Ativar a Pesquisa Inteligente",
"machine_learning_smart_search_enabled_description": "Se desativado, as imagens não serão codificadas para Pesquisa Inteligente.",
"machine_learning_url_description": "A URL do servidor de aprendizagem de máquina. Se for fornecido mais do que um URL, cada servidor será testado, um a um, até um deles responder com sucesso, por ordem do primeiro ao último. Servidores que não responderem serão temporariamente ignorados até voltarem a estar online.",
"maintenance_backup_management": "Gestão de cópias de segurança",
"maintenance_delete_backup": "Eliminar Cópia de Segurança",
"maintenance_delete_backup_description": "Este ficheiro irá ser apagado para sempre.",
"maintenance_delete_error": "Ocorreu um erro ao eliminar a cópia de segurança.",
"maintenance_integrity_check": "Verificar",
"maintenance_integrity_check_all": "Verificar tudo",
"maintenance_integrity_checksum_mismatch": "Checksum não corresponde",
"maintenance_integrity_checksum_mismatch_description": "Ficheiros dos quais a verificação checksum no disco não condiz com a que o Immich tem armazenado na base de dados.",
"maintenance_integrity_checksum_mismatch_job": "Verificar se existem erros de checksum",
"maintenance_integrity_checksum_mismatch_refresh_job": "Atualizar relatórios de erros de checksum",
"maintenance_integrity_missing_file": "Ficheiros em falta",
"maintenance_integrity_missing_file_description": "Ficheiros dos quais o Immich está a monitorizar na sua base de dados mas que não existem no sistema de ficheiros.",
"maintenance_integrity_missing_file_job": "Verificar se existem ficheiros em falta",
"maintenance_integrity_missing_file_refresh_job": "Atualizar relatórios de ficheiros em falta",
"maintenance_integrity_report": "Relatório de integridade",
"maintenance_integrity_untracked_file": "Ficheiros não monitorizados",
"maintenance_integrity_untracked_file_description": "Ficheiros nos diretórios do Immich dos quais não existe qualquer registo.",
"maintenance_integrity_untracked_file_job": "Verificar se existem ficheiros não monitorizados",
"maintenance_integrity_untracked_file_refresh_job": "Atualizar relatórios de ficheiros não monitorizados",
"maintenance_restore_backup": "Restaurar Cópia de Segurança",
@@ -655,7 +660,7 @@
"backup_background_service_complete_notification": "Cópia de conteúdos concluída",
"backup_background_service_connection_failed_message": "Ocorreu um erro na ligação ao servidor. A tentar de novo…",
"backup_background_service_current_upload_notification": "A enviar {filename}",
"backup_background_service_default_notification": "A verificar se há novos ficheiros…",
"backup_background_service_default_notification": "A verificar a existência de novos ficheiros…",
"backup_background_service_error_title": "Erro na cópia de segurança",
"backup_background_service_in_progress_notification": "A fazer cópia de segurança dos seus ficheiros…",
"backup_background_service_upload_failure_notification": "Ocorreu um erro ao enviar {filename}",
@@ -778,8 +783,8 @@
"changed_visibility_successfully": "Visibilidade alterada com sucesso",
"charging": "A carregar",
"charging_requirement_mobile_backup": "Cópia de segurança de fundo necessita que o dispositivo esteja a carregar",
"check_corrupt_asset_backup": "Verificar por backups corrompidos",
"check_corrupt_asset_backup_button": "Verificar",
"check_corrupt_asset_backup": "Verificar por cópias de segurança de ficheiros corrompidas",
"check_corrupt_asset_backup_button": "Realizar verificação",
"check_corrupt_asset_backup_description": "Execute esta verificação apenas numa rede Wi-Fi e quando a cópia de segurança de todos os ficheiros já estiver concluída. O processo pode demorar alguns minutos.",
"check_logs": "Verificar registos",
"checksum": "Teste de soma de dados",
+5
View File
@@ -189,18 +189,23 @@
"machine_learning_smart_search_enabled": "Povoliť inteligentné vyhľadávanie",
"machine_learning_smart_search_enabled_description": "Ak je vypnuté, obrázky nebudú spracované pre inteligentné vyhľadávanie.",
"machine_learning_url_description": "URL adresa servera strojového učenia. Ak je zadaných viacero adries URL, každý server bude testovaný postupne, kým jeden z nich neodpovie úspešne, v poradí od prvého po posledný. Servery, ktoré neodpovedajú, budú dočasne ignorované, kým nebudú opäť online.",
"maintenance_backup_management": "Spravovanie záloh",
"maintenance_delete_backup": "Vymazať zálohu",
"maintenance_delete_backup_description": "Tento súbor bude nezvratne vymazaný.",
"maintenance_delete_error": "Nepodarilo sa vymazať zálohu.",
"maintenance_integrity_check": "Skontrolovať",
"maintenance_integrity_check_all": "Skontrolovať všetko",
"maintenance_integrity_checksum_mismatch": "Nesúlad kontrolného súčtu",
"maintenance_integrity_checksum_mismatch_description": "Súbory, ktorých kontrolný súčet na disku sa nezhoduje s kontrolným súčtom, ktorý Immich uložil vo svojej databáze.",
"maintenance_integrity_checksum_mismatch_job": "Skontrolovať, či sa kontrolné súčty zhodujú",
"maintenance_integrity_checksum_mismatch_refresh_job": "Obnoviť správy o nezhode kontrolného súčtu",
"maintenance_integrity_missing_file": "Chýbajúce súbory",
"maintenance_integrity_missing_file_description": "Súbory, ktoré Immich zaznamenal vo svojej databáze, ale ktoré v súborovom systéme neexistujú.",
"maintenance_integrity_missing_file_job": "Skontrolovať chýbajúce súbory",
"maintenance_integrity_missing_file_refresh_job": "Obnoviť hlásenia o chýbajúcich súboroch",
"maintenance_integrity_report": "Hlásenie o integrite",
"maintenance_integrity_untracked_file": "Nesledované súbory",
"maintenance_integrity_untracked_file_description": "Súbory v adresároch Immich, o ktorých Immich nemá žiadne záznamy.",
"maintenance_integrity_untracked_file_job": "Skontrolovať nesledované súbory",
"maintenance_integrity_untracked_file_refresh_job": "Obnoviť hlásenia o nesledovaných súboroch",
"maintenance_restore_backup": "Obnoviť zálohu",
+5
View File
@@ -189,18 +189,23 @@
"machine_learning_smart_search_enabled": "Omogoči pametno iskanje",
"machine_learning_smart_search_enabled_description": "Če je onemogočeno, slike ne bodo kodirane za pametno iskanje.",
"machine_learning_url_description": "URL strežnika za strojno učenje. Če je na voljo več kot en URL, bo vsak strežnik poskusen posamično, dokler se eden ne odzove uspešno, v vrstnem redu od prvega do zadnjega. Strežniki, ki se ne odzovejo, bodo začasno prezrti, dokler se spet ne vzpostavijo.",
"maintenance_backup_management": "Upravljanje varnostnih kopij",
"maintenance_delete_backup": "Izbriši varnostno kopijo",
"maintenance_delete_backup_description": "Ta datoteka bo nepreklicno izbrisana.",
"maintenance_delete_error": "Varnostne kopije ni bilo mogoče izbrisati.",
"maintenance_integrity_check": "Preveri",
"maintenance_integrity_check_all": "Označi vse",
"maintenance_integrity_checksum_mismatch": "Neujemanje kontrolne vsote",
"maintenance_integrity_checksum_mismatch_description": "Datoteke, katerih kontrolna vsota na disku se ne ujema s kontrolno vsoto, ki jo je Immich shranil v svojo zbirko podatkov.",
"maintenance_integrity_checksum_mismatch_job": "Preverite neujemanje kontrolnih vsot",
"maintenance_integrity_checksum_mismatch_refresh_job": "Osveži poročila o neujemanju kontrolnih vsot",
"maintenance_integrity_missing_file": "Manjkajoče datoteke",
"maintenance_integrity_missing_file_description": "Datoteke, ki jih je Immich sledil v svoji bazi podatkov, vendar ne obstajajo v datotečnem sistemu.",
"maintenance_integrity_missing_file_job": "Preverite manjkajoče datoteke",
"maintenance_integrity_missing_file_refresh_job": "Osveži poročila o manjkajočih datotekah",
"maintenance_integrity_report": "Poročilo o integriteti",
"maintenance_integrity_untracked_file": "Nesledljive datoteke",
"maintenance_integrity_untracked_file_description": "Datoteke v Immichovih imenikih, o katerih Immich nima nobenih zapisov.",
"maintenance_integrity_untracked_file_job": "Preveri nesledljive datoteke",
"maintenance_integrity_untracked_file_refresh_job": "Osveži poročila o nesledenih datotekah",
"maintenance_restore_backup": "Obnovi varnostno kopijo",
+36 -1
View File
@@ -79,6 +79,7 @@
"cron_expression_description": "Sätt skanningsintervallet genom att använda cron-format. För mer information se <link>Crontab Guru</link>",
"cron_expression_presets": "Cron-uttryck förinställningar",
"disable_login": "Inaktivera inloggning",
"download_csv": "Ladda ner CSV",
"duplicate_detection_job_description": "Kör maskininlärning på objekt för att upptäcka liknande bilder. Bygger på Smart Search",
"exclusion_pattern_description": "Exkluderingsmönster tillåter dig att ignorera filer och mappar när skanning görs av ditt album. Detta är användbart om du har mappar som innehåller filer som du inte vill importera, t.ex. RAW-filer.",
"export_config_as_json_description": "Ladda ner den aktuella systemkonfigurationen som en JSON-fil",
@@ -188,9 +189,25 @@
"machine_learning_smart_search_enabled": "Aktivera smart sökning",
"machine_learning_smart_search_enabled_description": "Om inaktiverat kommer bilder inte att kodas för smart sökning.",
"machine_learning_url_description": "Maskininlärningsserverns URL. Om det är mer än en URL tillagd så kommer ett försök per URL att utföras tills någon av dom svarar, försöken görs i kronologisk ordning. Servrar som inte svarar kommer tillfälligt ignoreras tills de är nåbara igen.",
"maintenance_backup_management": "Hantering av säkerhetskopior",
"maintenance_delete_backup": "Ta bort säkerhetskopia",
"maintenance_delete_backup_description": "Den här filen kommer att raderas oåterkalleligt.",
"maintenance_delete_error": "Det gick inte att ta bort säkerhetskopian.",
"maintenance_integrity_check": "Kontrollera",
"maintenance_integrity_check_all": "Kontrollera alla",
"maintenance_integrity_checksum_mismatch": "Kontrollsummeavvikelse",
"maintenance_integrity_checksum_mismatch_description": "Filer vars kontrollsumma på disken inte stämmer överens med den kontrollsumma som Immich har lagrat i sin databas.",
"maintenance_integrity_checksum_mismatch_job": "Kolla efter kontrollsummor som inte matchar",
"maintenance_integrity_checksum_mismatch_refresh_job": "Uppdatera rapporter om kontrollsummor som inte matchar",
"maintenance_integrity_missing_file": "Filer saknas",
"maintenance_integrity_missing_file_description": "Filer som har registrerats i Immichs databas men som saknas i filsystemet.",
"maintenance_integrity_missing_file_job": "Kolla efter saknade filer",
"maintenance_integrity_missing_file_refresh_job": "Uppdatera rapporter om saknade filer",
"maintenance_integrity_report": "Integritetsrapport",
"maintenance_integrity_untracked_file": "Ospårade filer",
"maintenance_integrity_untracked_file_description": "Okända filer i Immichs kataloger.",
"maintenance_integrity_untracked_file_job": "Kolla efter okända filer",
"maintenance_integrity_untracked_file_refresh_job": "Uppdatera rapporter om okända filer",
"maintenance_restore_backup": "Återställ säkerhetskopia",
"maintenance_restore_backup_description": "Immich kommer att återställas från den valda säkerhetskopian. En ny säkerhetskopia kommer att skapas innan du fortsätter.",
"maintenance_restore_backup_different_version": "Denna säkerhetskopia skapades med en annan version av Immich!",
@@ -570,10 +587,11 @@
"asset_added_to_album": "Lades till i album",
"asset_adding_to_album": "Lägger till i album…",
"asset_created": "Objekt skapad",
"asset_day_count": "{date}: {count, plural, one {# objekt} other {# objekt}}",
"asset_description_updated": "Objektbeskrivning har uppdaterats",
"asset_filename_is_offline": "Objektet {filename} är offline",
"asset_has_unassigned_faces": "Objektet har otilldelade ansikten",
"asset_hashing": "Hashning…",
"asset_hashing": "Hashar…",
"asset_list_group_by_sub_title": "Gruppera på",
"asset_list_layout_settings_dynamic_layout_title": "Dynamisk layout",
"asset_list_layout_settings_group_automatically": "Automatiskt",
@@ -699,6 +717,7 @@
"backup_settings_subtitle": "Hantera uppladdningsinställningar",
"backup_upload_details_page_more_details": "Tryck för mer detaljer",
"backward": "Bakåt",
"battery_optimization_backup_reliability": "Inaktivera batterioptimeringar för stabilare säkerhetskopiering i bakgrunden",
"biometric_auth_enabled": "Biometrisk autentisering aktiverad",
"biometric_locked_out": "Du är utelåst från biometrisk autentisering",
"biometric_no_options": "Inga biometriska alternativ tillgängliga",
@@ -913,6 +932,8 @@
"deduplicate_all": "Deduplicera alla",
"default_locale": "Standardspråk",
"default_locale_description": "Formatera datum och siffror baserat på din webbläsares språkinställningar",
"default_quality_subtitle": "Kvalitet som används vid delning. Håll ner delaknappen för att välja kvalitet varje gång.",
"default_share_quality": "Standarddelningskvalitet",
"delete": "Radera",
"delete_action_confirmation_message": "Är du säker på att du vill ta bort det här objektet? Den här åtgärden flyttar objektet till serverns papperskorg och frågar om du vill ta bort den lokalt",
"delete_action_prompt": "{count} raderade",
@@ -1222,6 +1243,7 @@
"failed": "Misslyckades",
"failed_count": "Misslyckade: {count}",
"failed_to_authenticate": "Misslyckades med autentisering",
"failed_to_delete_file": "Misslyckades att radera fil",
"failed_to_load_assets": "Det gick inte att läsa in objekten",
"failed_to_load_folder": "Kunde inte ladda mappen",
"favorite": "Favorit",
@@ -1352,6 +1374,7 @@
"individual_share": "Enskild delning",
"individual_shares": "Individuella delningar",
"info": "Information",
"integrity_checks": "Integritetskontroller",
"interval": {
"day_at_onepm": "Alla dagar vid kl 13.00",
"hours": "Vid varje {hours, plural, one {hour} other {{hours, number} hours}}",
@@ -1399,6 +1422,7 @@
"leave": "Lämna",
"leave_album": "Lämna albumet",
"lens_model": "Objektiv",
"less": "Mindre",
"let_others_respond": "Låt andra svara",
"level": "Nivå",
"library": "Bibliotek",
@@ -1423,6 +1447,7 @@
"linked_oauth_account": "Länkat OAuth konto",
"list": "Lista",
"live": "Live",
"load_more": "Ladda mer",
"loading": "Inläsning",
"loading_search_results_failed": "Det gick inte att läsa in sökresultat",
"local": "Lokalt",
@@ -1689,6 +1714,7 @@
"not_selected": "Ej vald",
"notes": "Notera",
"nothing_here_yet": "Inget här ännu",
"notification_backup_reliability": "Aktivera aviseringar för att förbättra säkerhetskopieringen i bakgrunden",
"notification_permission_dialog_content": "För att aktivera notiser, gå till Inställningar och välj tillåt.",
"notification_permission_list_tile_content": "Tillåt rättighet för att slå på notiser.",
"notification_permission_list_tile_enable_button": "Aktivera Notiser",
@@ -2080,6 +2106,7 @@
"select_person": "Välj person",
"select_person_to_tag": "Välj en person att tagga",
"select_photos": "Välj foton",
"select_quality": "Välj kvalitet",
"select_trash_all": "Släng alla",
"select_user_for_sharing_page_err_album": "Kunde inte skapa nytt album",
"selected": "Valda",
@@ -2143,6 +2170,8 @@
"share_assets_selected": "{count} valda",
"share_dialog_preparing": "Förbereder...",
"share_link": "Dela Länk",
"share_original": "Använd original (stor)",
"share_preview": "Använd miniatyrbild (liten)",
"shared": "Delad",
"shared_album_activities_input_disable": "Kommentar är inaktiverad",
"shared_album_activity_remove_content": "Vill du ta bort den här aktiviteten?",
@@ -2244,6 +2273,7 @@
"slideshow_repeat_description": "Gå tillbaka till början när bildspelet slutar",
"slideshow_settings": "Bildspelsinställningar",
"smart_album": "Smart album",
"some_assets_already_have_a_location_warning": "Vissa av de valda objekten har redan platsdata",
"sort_albums_by": "Sortera album efter...",
"sort_created": "Skapat datum",
"sort_items": "Antal artiklar",
@@ -2364,6 +2394,8 @@
"trash_page_title": "Papperskorg ({count})",
"trashed_items_will_be_permanently_deleted_after": "Objekt i papperskorgen raderas permanent efter {days, plural, one {# dag} other {# dagar}}.",
"trigger": "Utlösare",
"trigger_asset_metadata_extraction": "Hämtning av objektmetadata",
"trigger_asset_metadata_extraction_description": "Aktiveras när EXIF-metadata hämtas från ett objekt",
"trigger_asset_uploaded": "Objekt uppladdning",
"trigger_asset_uploaded_description": "Utlöses när ett nytt objekt laddas upp",
"trigger_description": "Ett evenemang som sätter igång arbetsflödet",
@@ -2410,6 +2442,7 @@
"updated_password": "Lösenordet har uppdaterats",
"upload": "Ladda upp",
"upload_concurrency": "Uppladdning samtidighet",
"upload_day_count": "{date}: {count, plural, one {# uppladdning} other {# uppladdningar}}",
"upload_details": "Uppladdningsdetaljer",
"upload_dialog_info": "Vill du säkerhetskopiera de valda objekten till servern?",
"upload_dialog_title": "Ladda Upp Objekt",
@@ -2425,6 +2458,8 @@
"upload_to_immich": "Ladda upp till Immich ({count})",
"uploading": "Laddar upp",
"uploading_media": "Uppladdning av media",
"uploads": "Uppladdningar",
"uploads_count": "{count, plural, one {# uppladdning} other {# uppladdningar}}",
"url": "URL",
"usage": "Användning",
"use_biometric": "Använd biometri",
+6 -2
View File
@@ -79,6 +79,7 @@
"cron_expression_description": "ตั้งช่วงเวลาในการสแกนโดยใช้รูปแบบ cron สำหรับข้อมูลเพิ่มเติมกรุณาอิง <link>Crontab Guru</link>",
"cron_expression_presets": "พรีเซ็ตรูปแบบ cron",
"disable_login": "ปิดการล็อกอิน",
"download_csv": "ดาวน์โหลด CSV",
"duplicate_detection_job_description": "ใช้ machine learning กับสี่อเพื่อตรวจจับรูปภาพที่คล้ายกัน โดยใช้การค้นหาอัจฉริยะ",
"exclusion_pattern_description": "ข้อยกเว้นสามารถละเว้นไฟล์และโฟลเดอร์ขณะสแกนคลังภาพของคุณ มีประโยชน์เมื่อโฟลเดอร์มีไฟล์ที่ไม่อยากนำเข้า เช่นไฟล์ RAW",
"export_config_as_json_description": "ดาวน์โหลดการตั้งค่าระบบปัจจุบันไปยังไฟล์ในรูปแบบ JSON",
@@ -188,9 +189,12 @@
"machine_learning_smart_search_enabled": "เปิดใช้งานการค้นหาอัจฉริยะ",
"machine_learning_smart_search_enabled_description": "หากปิดใช้งาน ภาพจะไม่ถูกใช้สําหรับการค้นหาอัจฉริยะ",
"machine_learning_url_description": "URL ของเซิร์ฟเวอร์ machine learning กรณีมี URL มากกว่าหนึ่ง URL จะทำการทดลองส่งข้อมูลเรียงไปทีละอันตามลำดับจนกว่าจะพบ URL ที่ตอบสนอง และจะเลิกส่งข้อมูลชั่วคราวในส่วนของ URL ที่ไม่ตอบสนอง",
"maintenance_backup_management": "การจัดการสำรองข้อมูล",
"maintenance_delete_backup": "ลบการสำรองข้อมูล",
"maintenance_delete_backup_description": "ไฟล์นี้จะถูกลบและไม่สามารถย้อนกลับได้",
"maintenance_delete_error": "ไม่สามารถลบการสำรองข้อมูล",
"maintenance_integrity_check": "ตรวจสอบ",
"maintenance_integrity_check_all": "ตรวจสอบทั้งหมด",
"maintenance_restore_backup": "กู้คืนการสำรองข้อมูล",
"maintenance_restore_backup_description": "Immich จะถูกล้าง และกู้คืนข้อมูลจากการสำรองข้อมูลที่เลือก ระบบจะสร้างการสำรองข้อมูลก่อนดำเนินการต่อ",
"maintenance_restore_backup_different_version": "การสำรองข้อมูลนี้ถูกสร้างด้วย Immich เวอร์ชันต่างกัน!",
@@ -638,7 +642,7 @@
"backup_controller_page_background_battery_info_title": "ประสิทธิภาพแบตเตอรี่",
"backup_controller_page_background_charging": "ขณะชาร์จอย่างเดียว",
"backup_controller_page_background_configure_error": "ไม่สามารถติดตั้งบริการเบื้องหลัง",
"backup_controller_page_background_delay": "ล่าช้าการำรองทรัพยากรใหม่: {duration}",
"backup_controller_page_background_delay": "ล่าช้าการำรองทรัพยากรใหม่: {duration}",
"backup_controller_page_background_description": "เปิดบริการเบื้องหลังเพื่อที่จะสำรองทรัพยากรใหม่โดยที่ไม่จำเป็นต้องเปิดแอป",
"backup_controller_page_background_is_off": "การสำรองข้อมูลอัตโนมัติปิดอยู่",
"backup_controller_page_background_is_on": "การสำรองข้อมูลอัตโนมัติเปิดอยู่",
@@ -2142,7 +2146,7 @@
"sort_modified": "จัดเรียงตามวันที่แก้ไข",
"sort_newest": "รูปภาพใหม่ล่าสุด",
"sort_oldest": "จัดเรียงตามเก่าสุด",
"sort_people_by_similarity": "จดเรียงบุคคลตามความคล้ายคลึง",
"sort_people_by_similarity": "จดเรียงผู้คนตามความคล้ายคลึง",
"sort_recent": "จัดเรียงใหม่ล่าสุด",
"sort_title": "ไตเติ้ล",
"source": "แหล่ง",
+1 -1
View File
@@ -83,7 +83,7 @@
"exclusion_pattern_description": "Kütüphaneyi tararken dosya ve klasörleri görmezden gelmek için dışlama desenlerini kullanabilirsiniz. RAW dosyaları gibi bazı dosya ve klasörleri içe aktarmak istemediğinizde bu seçeneği kullanabilirsiniz.",
"export_config_as_json_description": "Geçerli sistem yapılandırmasını JSON dosyası olarak indir",
"external_libraries_page_description": "Yönetici harici kütüphane sayfası",
"face_detection": "Yüz tarama",
"face_detection": "Yüz tespiti",
"face_detection_description": "Makine öğrenimi kullanarak varlıklardaki yüzleri tespit et. Videolar için sadece küçük resim (thumbnail) dikkate alınır. 'Yenile' tüm varlıkları yeniden işler. 'Sıfırla', mevcut tüm yüz verilerini temizleyerek işlemi yeniden başlatır. 'Eksik' henüz işlenmemiş varlıkları sıraya alır. Tespit edilen yüzler, Yüz Tanıma işlemi tamamlandıktan sonra mevcut ya da yeni kişilere gruplanmak üzere Yüz Tanıma için sıraya alınacaktır.",
"facial_recognition_job_description": "Algılanan yüzleri kişilere grupla. Bu adım, Yüz Tespit işlemi tamamlandıktan sonra çalışır. \"Sıfırla\", tüm yüzleri yeniden gruplandırır. \"Eksik\" ise henüz bir kişiye atanmamış yüzleri sıraya alır.",
"failed_job_command": "{job} görevi için {command} komutu başarısız",
+45 -2
View File
@@ -51,7 +51,7 @@
"authentication_settings": "驗證設定",
"authentication_settings_description": "管理密碼、OAuth 同其他驗證設定",
"authentication_settings_disable_all": "你確定你要停用所有登入嘅方式嗎?你會完全冇辦法登入。",
"authentication_settings_reenable": "如果你要重新啟用嘅話,請使用<link>伺服器指令</link>。",
"authentication_settings_reenable": "如果你要重新啟用嘅話,請使用<link>伺服器指令</link>。",
"background_task_job": "背景操作",
"backup_database": "建立資料庫備份",
"backup_database_enable_description": "啟用資料庫備份",
@@ -60,7 +60,7 @@
"backup_onboarding_2_description": "儲存係唔同裝置嘅本地副本。包含主要嘅檔案同埋喺本機嘅備份。",
"backup_onboarding_3_description": "資料包含原始文件,總共備份嘅次數。呢個包括1份異地嘅備份同埋2份本機副本。",
"backup_onboarding_description": "我哋建議你使用<backblaze-link>嘅3-2-1備份</backblaze-link>嚟保障你嘅資料。為咗可以全方位地備份你嘅資料,你應該保留已經上傳嘅相/影片嘅副本,同埋Immich嘅資料庫,。",
"backup_onboarding_footer": "有關其他Immich備份嘅資料,請參考<link></link>。",
"backup_onboarding_footer": "有關其他Immich備份嘅資料,請參考<link>說明文件</link>。",
"backup_onboarding_parts_title": "一個3-2-1備份包括:",
"backup_onboarding_title": "備份",
"backup_settings": "資料庫備份嘅設定",
@@ -261,6 +261,49 @@
"nightly_tasks_sync_quota_usage_setting": "同步限額使用情況",
"nightly_tasks_sync_quota_usage_setting_description": "根據目前嘅使用量更新使用者嘅儲存限額",
"no_paths_added": "冇新增咗嘅路徑",
"no_pattern_added": "未有排除規則",
"note_apply_storage_label_previous_assets": "提示:如果您想將儲存標籤套用到上傳咗嘅項目,請執行",
"note_cannot_be_changed_later": "注意:呢個設定之後改唔到!",
"notification_email_from_address": "寄件地址",
"notification_email_from_address_description": "寄件者電郵,好似:\"Immich Photo Server <noreply@example.com>\"。請確保係您有權限寄送到郵件嘅地址。",
"notification_email_host_description": "電子郵件伺服器主機(例如:smtp.immich.app",
"notification_email_ignore_certificate_errors": "忽略唔啱嘅憑證",
"notification_email_ignore_certificate_errors_description": "忽略 TLS 錯嘅憑證驗證(唔建議)",
"notification_email_password_description": "用嚟驗證電郵伺服器嘅密碼",
"notification_email_port_description": "電郵伺服器嘅連接埠(例如 25、465 或 587",
"notification_email_secure": "SMTPS",
"notification_email_secure_description": "用 SMTPS(基於 TLS 嘅 SMTP",
"notification_email_sent_test_email_button": "傳送測試電郵,跟住儲存",
"notification_email_setting_description": "寄送電郵通知嘅設定",
"notification_email_test_email": "傳送測試電郵",
"notification_email_test_email_failed": "傳送唔到測試電郵,請檢查您嘅設定",
"notification_email_test_email_sent": "測試電郵已傳送咗到 {email}。請檢查您嘅收件匣。",
"notification_email_username_description": "用嚟驗證電郵伺服器嘅使用者名稱",
"notification_enable_email_notifications": "用電郵通知",
"notification_settings": "通知設定",
"notification_settings_description": "管理通知設定,包括電郵設定",
"oauth_allow_insecure_requests": "畀用唔可靠嘅請求",
"oauth_allow_insecure_requests_description": "警告:呢個選項會停用 OAuth 嘅 TLS 憑證驗證請求,可能會令您暴露於 MITM 攻擊。",
"oauth_auto_launch": "自動啟動",
"oauth_auto_launch_description": "進入登入頁面時,自動啟動 OAuth 登入流程",
"oauth_auto_register": "自動註冊",
"oauth_auto_register_description": "使用 OAuth 登入之後自動註冊新使用者",
"oauth_button_text": "按鈕文字",
"oauth_client_secret_description": "機密用戶端嘅必填項目;如果公開用戶端唔支援 PKCE (代碼交換的驗證金鑰),必須填寫。",
"oauth_enable_description": "用 OAuth 登入",
"oauth_end_session_url_description": "用戶登出時將佢導向到呢個URL。",
"oauth_mobile_redirect_uri": "流動裝置重新導向 URI",
"oauth_mobile_redirect_uri_override": "覆寫流動裝置重新導向 URI",
"oauth_mobile_redirect_uri_override_description": "當 OAuth 提供者唔俾用流動裝置 URI(例如 ''{callback}'')嘅時候啟用",
"oauth_prompt_description": "Prompt 參數(例如 select_account、login、consent",
"oauth_role_claim": "角色宣告",
"oauth_role_claim_description": "根據呢個宣告嘅存在,自動授權管理員權限。呢啲宣告值可以係 'user' 或 'admin'。",
"oauth_settings": "OAuth",
"oauth_settings_description": "管理 OAuth 登入設定",
"oauth_settings_more_details": "如果想了解呢個功能嘅詳細資訊,請參閱 <link>說明文件</link>。",
"oauth_storage_label_claim": "儲存標籤宣告",
"oauth_storage_label_claim_description": "自動將使用者嘅儲存標籤設定為呢個宣告值。",
"oauth_storage_quota_claim": "儲存限額宣告",
"queue_details": "隊列資訊",
"queues": "任務隊列",
"queues_page_description": "管理員任務隊列頁面",
+1 -1
View File
@@ -1,6 +1,6 @@
[project]
name = "immich-ml"
version = "3.0.0rc1"
version = "3.0.0rc2"
description = ""
authors = [{ name = "Hau Tran", email = "alex.tran1502@gmail.com" }]
requires-python = ">=3.11,<4.0"
+1 -1
View File
@@ -974,7 +974,7 @@ wheels = [
[[package]]
name = "immich-ml"
version = "3.0.0rc1"
version = "3.0.0rc2"
source = { editable = "." }
dependencies = [
{ name = "aiocache" },
+28 -52
View File
@@ -83,17 +83,12 @@ version = "7.1.3-6"
backend = "github:jellyfin/jellyfin-ffmpeg"
[tools."github:jellyfin/jellyfin-ffmpeg".options]
asset_pattern = "jellyfin-ffmpeg_*_portable_linuxarm64-gpl.tar.xz"
asset_pattern = "jellyfin-ffmpeg_*_portable_macarm64-gpl.tar.xz"
[tools."github:jellyfin/jellyfin-ffmpeg"."platforms.linux-arm64"]
checksum = "sha256:bea03c670e8cc5bfe9edc0c5d624d4735421610cef5e808db93e7d8596952886"
url = "https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v7.1.3-6/jellyfin-ffmpeg_7.1.3-6_portable_linuxarm64-gpl.tar.xz"
url_api = "https://api.github.com/repos/jellyfin/jellyfin-ffmpeg/releases/assets/409048876"
[tools."github:jellyfin/jellyfin-ffmpeg"."platforms.linux-arm64-musl"]
checksum = "sha256:bea03c670e8cc5bfe9edc0c5d624d4735421610cef5e808db93e7d8596952886"
url = "https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v7.1.3-6/jellyfin-ffmpeg_7.1.3-6_portable_linuxarm64-gpl.tar.xz"
url_api = "https://api.github.com/repos/jellyfin/jellyfin-ffmpeg/releases/assets/409048876"
[tools."github:jellyfin/jellyfin-ffmpeg"."platforms.macos-arm64"]
checksum = "sha256:e024d5e78d5414e75f0181036cd21373fafb9270c72894dfd7dbda2572439820"
url = "https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v7.1.3-6/jellyfin-ffmpeg_7.1.3-6_portable_macarm64-gpl.tar.xz"
url_api = "https://api.github.com/repos/jellyfin/jellyfin-ffmpeg/releases/assets/408995838"
[[tools."github:jellyfin/jellyfin-ffmpeg"]]
version = "7.1.3-6"
@@ -117,12 +112,17 @@ version = "7.1.3-6"
backend = "github:jellyfin/jellyfin-ffmpeg"
[tools."github:jellyfin/jellyfin-ffmpeg".options]
asset_pattern = "jellyfin-ffmpeg_*_portable_macarm64-gpl.tar.xz"
asset_pattern = "jellyfin-ffmpeg_*_portable_linuxarm64-gpl.tar.xz"
[tools."github:jellyfin/jellyfin-ffmpeg"."platforms.macos-arm64"]
checksum = "sha256:e024d5e78d5414e75f0181036cd21373fafb9270c72894dfd7dbda2572439820"
url = "https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v7.1.3-6/jellyfin-ffmpeg_7.1.3-6_portable_macarm64-gpl.tar.xz"
url_api = "https://api.github.com/repos/jellyfin/jellyfin-ffmpeg/releases/assets/408995838"
[tools."github:jellyfin/jellyfin-ffmpeg"."platforms.linux-arm64"]
checksum = "sha256:bea03c670e8cc5bfe9edc0c5d624d4735421610cef5e808db93e7d8596952886"
url = "https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v7.1.3-6/jellyfin-ffmpeg_7.1.3-6_portable_linuxarm64-gpl.tar.xz"
url_api = "https://api.github.com/repos/jellyfin/jellyfin-ffmpeg/releases/assets/409048876"
[tools."github:jellyfin/jellyfin-ffmpeg"."platforms.linux-arm64-musl"]
checksum = "sha256:bea03c670e8cc5bfe9edc0c5d624d4735421610cef5e808db93e7d8596952886"
url = "https://github.com/jellyfin/jellyfin-ffmpeg/releases/download/v7.1.3-6/jellyfin-ffmpeg_7.1.3-6_portable_linuxarm64-gpl.tar.xz"
url_api = "https://api.github.com/repos/jellyfin/jellyfin-ffmpeg/releases/assets/409048876"
[[tools."github:jellyfin/jellyfin-ffmpeg"]]
version = "7.1.3-6"
@@ -188,30 +188,6 @@ url_api = "https://api.github.com/repos/WebAssembly/binaryen/releases/assets/288
version = "21.0.2"
backend = "core:java"
[tools.java."platforms.linux-arm64"]
checksum = "sha256:08db1392a48d4eb5ea5315cf8f18b89dbaf36cda663ba882cf03c704c9257ec2"
url = "https://download.java.net/java/GA/jdk21.0.2/f2283984656d49d69e91c558476027ac/13/GPL/openjdk-21.0.2_linux-aarch64_bin.tar.gz"
[tools.java."platforms.linux-x64"]
checksum = "sha256:a2def047a73941e01a73739f92755f86b895811afb1f91243db214cff5bdac3f"
url = "https://download.java.net/java/GA/jdk21.0.2/f2283984656d49d69e91c558476027ac/13/GPL/openjdk-21.0.2_linux-x64_bin.tar.gz"
[tools.java."platforms.macos-arm64"]
checksum = "sha256:b3d588e16ec1e0ef9805d8a696591bd518a5cea62567da8f53b5ce32d11d22e4"
url = "https://download.java.net/java/GA/jdk21.0.2/f2283984656d49d69e91c558476027ac/13/GPL/openjdk-21.0.2_macos-aarch64_bin.tar.gz"
[tools.java."platforms.macos-x64"]
checksum = "sha256:8fd09e15dc406387a0aba70bf5d99692874e999bf9cd9208b452b5d76ac922d3"
url = "https://download.java.net/java/GA/jdk21.0.2/f2283984656d49d69e91c558476027ac/13/GPL/openjdk-21.0.2_macos-x64_bin.tar.gz"
[tools.java."platforms.windows-x64"]
checksum = "sha256:b6c17e747ae78cdd6de4d7532b3164b277daee97c007d3eaa2b39cca99882664"
url = "https://download.java.net/java/GA/jdk21.0.2/f2283984656d49d69e91c558476027ac/13/GPL/openjdk-21.0.2_windows-x64_bin.zip"
[[tools.java]]
version = "21.0.2"
backend = "core:java"
[tools.java.options]
shorthand_vendor = "openjdk"
@@ -304,37 +280,37 @@ checksum = "sha256:27323f70c875b8251bfd7e61a4cffc3ebff4e56ed1e611b955016f0c70773
url = "https://github.com/opentofu/opentofu/releases/download/v1.11.6/tofu_1.11.6_windows_amd64.tar.gz"
[[tools.pnpm]]
version = "11.5.2"
version = "11.6.0"
backend = "aqua:pnpm/pnpm"
[tools.pnpm."platforms.linux-arm64"]
checksum = "sha256:7fef0c74081135d777754fccf25272f698e504b26ba0568504846c0cea402f8f"
url = "https://github.com/pnpm/pnpm/releases/download/v11.5.2/pnpm-linux-arm64.tar.gz"
checksum = "sha256:2fec653ff6dadab340d1c3d2214688a7451cc471f39710839440b293ca7c53b0"
url = "https://github.com/pnpm/pnpm/releases/download/v11.6.0/pnpm-linux-arm64.tar.gz"
provenance = "github-attestations"
[tools.pnpm."platforms.linux-arm64-musl"]
checksum = "sha256:843beed7bca760276d29f8950ca219600995d345dbc93fad8150b3e5f83b74d4"
url = "https://github.com/pnpm/pnpm/releases/download/v11.5.2/pnpm-linux-arm64-musl.tar.gz"
checksum = "sha256:56a78c08cf22adf29e7dacb6f7f100139731693863d20fb94a7883463a62169e"
url = "https://github.com/pnpm/pnpm/releases/download/v11.6.0/pnpm-linux-arm64-musl.tar.gz"
provenance = "github-attestations"
[tools.pnpm."platforms.linux-x64"]
checksum = "sha256:2033a702618c8576dc6bb0f6adb3a67ab506031351ddd59ca50d1bcaf5d13dc5"
url = "https://github.com/pnpm/pnpm/releases/download/v11.5.2/pnpm-linux-x64.tar.gz"
checksum = "sha256:74d64c1646385fb21691f32f0ab6aca1a9f5c829ba54d3cda3a24838a228e68c"
url = "https://github.com/pnpm/pnpm/releases/download/v11.6.0/pnpm-linux-x64.tar.gz"
provenance = "github-attestations"
[tools.pnpm."platforms.linux-x64-musl"]
checksum = "sha256:0b794b23461c7475f7ffc29c4945692838b51ddadd857a2ace8edf0018798305"
url = "https://github.com/pnpm/pnpm/releases/download/v11.5.2/pnpm-linux-x64-musl.tar.gz"
checksum = "sha256:7a0c463a09d912fba6b7d9eca0a946bc228ea50f3015a05c09a29e7e85a932d7"
url = "https://github.com/pnpm/pnpm/releases/download/v11.6.0/pnpm-linux-x64-musl.tar.gz"
provenance = "github-attestations"
[tools.pnpm."platforms.macos-arm64"]
checksum = "sha256:54993dae26bea0f3c1b0e15f9427f6f6a86827d56f32d1d1554d8cda59a62399"
url = "https://github.com/pnpm/pnpm/releases/download/v11.5.2/pnpm-darwin-arm64.tar.gz"
checksum = "sha256:87c901635a14481fb30566a3749041134ffd4317bc6fe866c345b69fdf9b6b85"
url = "https://github.com/pnpm/pnpm/releases/download/v11.6.0/pnpm-darwin-arm64.tar.gz"
provenance = "github-attestations"
[tools.pnpm."platforms.windows-x64"]
checksum = "sha256:b3ddff2c2bf87d3996fadf074bac58cd2259f718a17912a04ae930e3775b30e9"
url = "https://github.com/pnpm/pnpm/releases/download/v11.5.2/pnpm-win32-x64.zip"
checksum = "sha256:91c753435542b04859c689304fae0dd64eba6b40937cfa426a48485b712e4e9c"
url = "https://github.com/pnpm/pnpm/releases/download/v11.6.0/pnpm-win32-x64.zip"
provenance = "github-attestations"
[[tools.terragrunt]]
+1 -1
View File
@@ -16,7 +16,7 @@ config_roots = [
[tools]
node = "24.15.0"
pnpm = "11.5.2"
pnpm = "11.6.0"
terragrunt = "1.0.3"
opentofu = "1.11.6"
"npm:oazapfts" = "7.5.0"
+4 -4
View File
@@ -22,8 +22,8 @@ platform :android do
task: 'bundle',
build_type: 'Release',
properties: {
"android.injected.version.code" => 3049,
"android.injected.version.name" => "3.0.0-rc.1",
"android.injected.version.code" => 3050,
"android.injected.version.name" => "3.0.0-rc.2",
}
)
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab', track: 'beta')
@@ -35,8 +35,8 @@ platform :android do
task: 'bundle',
build_type: 'Release',
properties: {
"android.injected.version.code" => 3049,
"android.injected.version.name" => "3.0.0-rc.1",
"android.injected.version.code" => 3050,
"android.injected.version.name" => "3.0.0-rc.2",
}
)
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
+24 -35
View File
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 77;
objects = {
/* Begin PBXBuildFile section */
@@ -11,6 +11,7 @@
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
467DA6EAF83F3481F8BD94AB /* Pods_ShareExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8AB817AA297EDEC88B23F3F6 /* Pods_ShareExtension.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
@@ -19,9 +20,9 @@
B21E34AC2E5B09190031FDB9 /* BackgroundWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = B21E34AB2E5B09100031FDB9 /* BackgroundWorker.swift */; };
B25D377A2E72CA15008B6CA7 /* Connectivity.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = B25D37782E72CA15008B6CA7 /* Connectivity.g.swift */; };
B25D377C2E72CA26008B6CA7 /* ConnectivityApiImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = B25D377B2E72CA20008B6CA7 /* ConnectivityApiImpl.swift */; };
B2BE315F2E5E5229006EEF88 /* BackgroundWorker.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2BE315E2E5E5229006EEF88 /* BackgroundWorker.g.swift */; };
B2EE00022E72CA15008B6CA7 /* PermissionApi.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2EE00012E72CA15008B6CA7 /* PermissionApi.g.swift */; };
B2EE00042E72CA15008B6CA7 /* PermissionApiImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2EE00032E72CA15008B6CA7 /* PermissionApiImpl.swift */; };
B2BE315F2E5E5229006EEF88 /* BackgroundWorker.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2BE315E2E5E5229006EEF88 /* BackgroundWorker.g.swift */; };
D3BED739C0BC29BB32E18EB2 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CC499FBCE6B29B2DAFED7130 /* Pods_Runner.framework */; };
F02538E92DFBCBDD008C3FA3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
F0B57D3A2DF764BD00DC5BCC /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F0B57D392DF764BD00DC5BCC /* WidgetKit.framework */; };
@@ -39,7 +40,6 @@
FEE084F82EC172460045228E /* SQLiteData in Frameworks */ = {isa = PBXBuildFile; productRef = FEE084F72EC172460045228E /* SQLiteData */; };
FEE084FB2EC1725A0045228E /* RawStructuredFieldValues in Frameworks */ = {isa = PBXBuildFile; productRef = FEE084FA2EC1725A0045228E /* RawStructuredFieldValues */; };
FEE084FD2EC1725A0045228E /* StructuredFieldValues in Frameworks */ = {isa = PBXBuildFile; productRef = FEE084FC2EC1725A0045228E /* StructuredFieldValues */; };
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -94,6 +94,7 @@
6D160F04A389B9FFBC557803 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
8AB817AA297EDEC88B23F3F6 /* Pods_ShareExtension.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ShareExtension.framework; sourceTree = BUILT_PRODUCTS_DIR; };
937632897A02DE9C249F20A6 /* Pods-ShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.debug.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
@@ -109,9 +110,9 @@
B21E34AB2E5B09100031FDB9 /* BackgroundWorker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundWorker.swift; sourceTree = "<group>"; };
B25D37782E72CA15008B6CA7 /* Connectivity.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Connectivity.g.swift; sourceTree = "<group>"; };
B25D377B2E72CA20008B6CA7 /* ConnectivityApiImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectivityApiImpl.swift; sourceTree = "<group>"; };
B2BE315E2E5E5229006EEF88 /* BackgroundWorker.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundWorker.g.swift; sourceTree = "<group>"; };
B2EE00012E72CA15008B6CA7 /* PermissionApi.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionApi.g.swift; sourceTree = "<group>"; };
B2EE00032E72CA15008B6CA7 /* PermissionApiImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PermissionApiImpl.swift; sourceTree = "<group>"; };
B2BE315E2E5E5229006EEF88 /* BackgroundWorker.g.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundWorker.g.swift; sourceTree = "<group>"; };
C4A6A71F33CE37B3C913115C /* Pods-ShareExtension.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ShareExtension.profile.xcconfig"; path = "Target Support Files/Pods-ShareExtension/Pods-ShareExtension.profile.xcconfig"; sourceTree = "<group>"; };
CC499FBCE6B29B2DAFED7130 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
F0B57D382DF764BD00DC5BCC /* WidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = WidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -130,7 +131,6 @@
FE5499F72F1198DE006016CB /* RemoteImagesImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteImagesImpl.swift; sourceTree = "<group>"; };
FE5FE4AD2F30FBC000A71243 /* ImageProcessing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageProcessing.swift; sourceTree = "<group>"; };
FEAFA8722E4D42F4001E47FE /* Thumbhash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thumbhash.swift; sourceTree = "<group>"; };
78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
@@ -153,15 +153,11 @@
/* Begin PBXFileSystemSynchronizedRootGroup section */
B231F52D2E93A44A00BC45D1 /* Core */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
);
path = Core;
sourceTree = "<group>";
};
B2CF7F8C2DDE4EBB00744BF6 /* Sync */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
);
path = Sync;
sourceTree = "<group>";
};
@@ -183,8 +179,6 @@
};
FEE084F22EC172080045228E /* Schemas */ = {
isa = PBXFileSystemSynchronizedRootGroup;
exceptions = (
);
path = Schemas;
sourceTree = "<group>";
};
@@ -364,9 +358,6 @@
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
packageProductDependencies = (
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
);
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
@@ -473,7 +464,7 @@
);
mainGroup = 97C146E51CF9000F007C117D;
packageReferences = (
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
FEE084F62EC172460045228E /* XCRemoteSwiftPackageReference "sqlite-data" */,
FEE084F92EC1725A0045228E /* XCRemoteSwiftPackageReference "swift-http-structured-headers" */,
);
@@ -528,10 +519,14 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
@@ -561,10 +556,14 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
@@ -758,7 +757,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = NO;
NEW_SETTING = "";
SDKROOT = iphoneos;
@@ -777,7 +776,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@@ -786,7 +785,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.121.0;
MARKETING_VERSION = 3.0.0;
PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.profile;
PRODUCT_NAME = "Immich-Profile";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -847,7 +846,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = YES;
NEW_SETTING = "";
ONLY_ACTIVE_ARCH = YES;
@@ -901,7 +900,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
MTL_ENABLE_DEBUG_INFO = NO;
NEW_SETTING = "";
SDKROOT = iphoneos;
@@ -922,7 +921,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@@ -931,7 +930,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.121.0;
MARKETING_VERSION = 3.0.0;
PRODUCT_BUNDLE_IDENTIFIER = app.futo.immich.debug;
PRODUCT_NAME = "Immich-Debug";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -951,7 +950,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 240;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_TEAM = 2W7AC6T8T5;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
@@ -960,7 +959,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.121.0;
MARKETING_VERSION = 3.0.0;
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich;
PRODUCT_NAME = Immich;
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1262,7 +1261,7 @@
/* End XCConfigurationList section */
/* Begin XCLocalSwiftPackageReference section */
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
};
@@ -1307,17 +1306,7 @@
package = FEE084F92EC1725A0045228E /* XCRemoteSwiftPackageReference "swift-http-structured-headers" */;
productName = StructuredFieldValues;
};
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
isa = XCSwiftPackageProductDependency;
productName = FlutterGeneratedPluginSwiftPackage;
};
/* End XCSwiftPackageProductDependency section */
/* Begin XCLocalSwiftPackageReference section */
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
};
/* End XCLocalSwiftPackageReference section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
+203 -202
View File
@@ -1,205 +1,206 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AppGroupId</key>
<string>$(CUSTOM_GROUP_ID)</string>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>app.alextran.immich.background.refreshUpload</string>
<string>app.alextran.immich.background.processingUpload</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>ShareHandler</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>public.file-url</string>
<string>public.image</string>
<string>public.text</string>
<string>public.movie</string>
<string>public.url</string>
<string>public.data</string>
</array>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>ar</string>
<string>ca</string>
<string>cs</string>
<string>da</string>
<string>de</string>
<string>es</string>
<string>fi</string>
<string>fr</string>
<string>he</string>
<string>hi</string>
<string>hu</string>
<string>it</string>
<string>ja</string>
<string>ko</string>
<string>lv</string>
<string>mn</string>
<string>nb</string>
<string>nl</string>
<string>pl</string>
<string>pt</string>
<string>ro</string>
<string>ru</string>
<string>sk</string>
<string>sl</string>
<string>sr</string>
<string>sv</string>
<string>th</string>
<string>uk</string>
<string>vi</string>
<string>zh</string>
</array>
<key>CFBundleName</key>
<string>immich_mobile</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>Share Extension</string>
<key>CFBundleURLSchemes</key>
<array>
<string>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>Deep Link</string>
<key>CFBundleURLSchemes</key>
<array>
<string>immich</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>240</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>https</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<string>No</string>
<key>MGLMapboxMetricsEnabledSettingShownInApp</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSBonjourServices</key>
<array>
<string>_googlecast._tcp</string>
<string>_CC1AD845._googlecast._tcp</string>
</array>
<key>NSCameraUsageDescription</key>
<string>We need to access the camera to let you take beautiful video using this app</string>
<key>NSFaceIDUsageDescription</key>
<string>We need to use FaceID to allow access to your locked folder</string>
<key>NSLocalNetworkUsageDescription</key>
<string>We need local network permission to connect to the local server using IP address and allow the casting feature to work</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We require this permission to access the local WiFi name for background upload mechanism</string>
<key>NSLocationUsageDescription</key>
<string>We require this permission to access the local WiFi name</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>We require this permission to access the local WiFi name</string>
<key>NSMicrophoneUsageDescription</key>
<string>We need to access the microphone to let you take beautiful video using this app</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>We need to manage backup your photos album</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need to manage backup your photos album</string>
<key>NSUserActivityTypes</key>
<array>
<string>INSendMessageIntent</string>
</array>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneDelegateClassName</key>
<string>FlutterSceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>processing</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIStatusBarHidden</key>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true/>
<key>io.flutter.embedded_views_preview</key>
<true/>
</dict>
</plist>
<dict>
<key>AppGroupId</key>
<string>$(CUSTOM_GROUP_ID)</string>
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>app.alextran.immich.background.refreshUpload</string>
<string>app.alextran.immich.background.processingUpload</string>
</array>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true />
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>ShareHandler</string>
<key>LSHandlerRank</key>
<string>Alternate</string>
<key>LSItemContentTypes</key>
<array>
<string>public.file-url</string>
<string>public.image</string>
<string>public.text</string>
<string>public.movie</string>
<string>public.url</string>
<string>public.data</string>
</array>
</dict>
</array>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>ar</string>
<string>ca</string>
<string>cs</string>
<string>da</string>
<string>de</string>
<string>es</string>
<string>fi</string>
<string>fr</string>
<string>he</string>
<string>hi</string>
<string>hu</string>
<string>it</string>
<string>ja</string>
<string>ko</string>
<string>lv</string>
<string>mn</string>
<string>nb</string>
<string>nl</string>
<string>pl</string>
<string>pt</string>
<string>ro</string>
<string>ru</string>
<string>sk</string>
<string>sl</string>
<string>sr</string>
<string>sv</string>
<string>th</string>
<string>uk</string>
<string>vi</string>
<string>zh</string>
</array>
<key>CFBundleName</key>
<string>immich_mobile</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>Share Extension</string>
<key>CFBundleURLSchemes</key>
<array>
<string>ShareMedia-$(PRODUCT_BUNDLE_IDENTIFIER)</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>Deep Link</string>
<key>CFBundleURLSchemes</key>
<array>
<string>immich</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>4</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false />
<key>LSApplicationQueriesSchemes</key>
<array>
<string>https</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true />
<key>LSSupportsOpeningDocumentsInPlace</key>
<string>No</string>
<key>MGLMapboxMetricsEnabledSettingShownInApp</key>
<true />
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true />
</dict>
<key>NSBonjourServices</key>
<array>
<string>_googlecast._tcp</string>
<string>_CC1AD845._googlecast._tcp</string>
</array>
<key>NSCameraUsageDescription</key>
<string>We need to access the camera to let you take beautiful video using this app</string>
<key>NSFaceIDUsageDescription</key>
<string>We need to use FaceID to allow access to your locked folder</string>
<key>NSLocalNetworkUsageDescription</key>
<string>We need local network permission to connect to the local server using IP address and
allow the casting feature to work</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We require this permission to access the local WiFi name for background upload mechanism</string>
<key>NSLocationUsageDescription</key>
<string>We require this permission to access the local WiFi name</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>We require this permission to access the local WiFi name</string>
<key>NSMicrophoneUsageDescription</key>
<string>We need to access the microphone to let you take beautiful video using this app</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>We need to manage backup your photos album</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>We need to manage backup your photos album</string>
<key>NSUserActivityTypes</key>
<array>
<string>INSendMessageIntent</string>
</array>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false />
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneDelegateClassName</key>
<string>FlutterSceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true />
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>processing</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIStatusBarHidden</key>
<false />
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<true />
<key>io.flutter.embedded_views_preview</key>
<true />
</dict>
</plist>
@@ -19,6 +19,11 @@ import 'package:immich_mobile/widgets/backup/drift_album_info_list_tile.dart';
import 'package:immich_mobile/widgets/common/search_field.dart';
import 'package:logging/logging.dart';
final backupAlbumCountProvider = FutureProvider.autoDispose<int>((ref) async {
await ref.read(backupAlbumProvider.notifier).getAll();
return ref.read(backupAlbumProvider).length;
});
@RoutePage()
class DriftBackupAlbumSelectionPage extends ConsumerStatefulWidget {
const DriftBackupAlbumSelectionPage({super.key});
@@ -44,7 +49,6 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
_searchFocusNode = FocusNode();
_enableSyncUploadAlbum.value = ref.read(appConfigProvider).backup.syncAlbums;
ref.read(backupAlbumProvider.notifier).getAll();
_initialTotalAssetCount = ref.read(driftBackupProvider.select((p) => p.totalCount));
}
@@ -79,6 +83,7 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
@override
Widget build(BuildContext context) {
final isLoading = ref.watch(backupAlbumCountProvider).isLoading;
final albums = ref.watch(backupAlbumProvider);
final albumCount = albums.length;
// Filter albums based on search query
@@ -246,15 +251,32 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
],
),
),
SliverLayoutBuilder(
builder: (context, constraints) {
if (constraints.crossAxisExtent > 600) {
return _AlbumSelectionGrid(filteredAlbums: filteredAlbums, searchQuery: _searchQuery);
} else {
return _AlbumSelectionList(filteredAlbums: filteredAlbums, searchQuery: _searchQuery);
}
},
),
if (filteredAlbums.isEmpty)
SliverToBoxAdapter(
child: Center(
child: _searchQuery.isNotEmpty
? Padding(
padding: const EdgeInsets.all(24.0),
child: Text('album_search_not_found'.t(context: context)),
)
: isLoading
? const CircularProgressIndicator()
: Padding(
padding: const EdgeInsets.all(24.0),
child: Text('no_albums_found'.t(context: context)),
),
),
)
else
SliverLayoutBuilder(
builder: (context, constraints) {
if (constraints.crossAxisExtent > 600) {
return _AlbumSelectionGrid(filteredAlbums: filteredAlbums);
} else {
return _AlbumSelectionList(filteredAlbums: filteredAlbums);
}
},
),
],
),
if (_handleLinkedAlbumFuture != null)
@@ -291,27 +313,11 @@ class _DriftBackupAlbumSelectionPageState extends ConsumerState<DriftBackupAlbum
class _AlbumSelectionList extends StatelessWidget {
final List<LocalAlbum> filteredAlbums;
final String searchQuery;
const _AlbumSelectionList({required this.filteredAlbums, required this.searchQuery});
const _AlbumSelectionList({required this.filteredAlbums});
@override
Widget build(BuildContext context) {
if (filteredAlbums.isEmpty && searchQuery.isNotEmpty) {
return SliverToBoxAdapter(
child: Center(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Text('album_search_not_found'.t(context: context)),
),
),
);
}
if (filteredAlbums.isEmpty) {
return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator()));
}
return SliverPadding(
padding: const EdgeInsets.symmetric(vertical: 12.0),
sliver: SliverList(
@@ -325,27 +331,11 @@ class _AlbumSelectionList extends StatelessWidget {
class _AlbumSelectionGrid extends StatelessWidget {
final List<LocalAlbum> filteredAlbums;
final String searchQuery;
const _AlbumSelectionGrid({required this.filteredAlbums, required this.searchQuery});
const _AlbumSelectionGrid({required this.filteredAlbums});
@override
Widget build(BuildContext context) {
if (filteredAlbums.isEmpty && searchQuery.isNotEmpty) {
return SliverToBoxAdapter(
child: Center(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Text('album_search_not_found'.t(context: context)),
),
),
);
}
if (filteredAlbums.isEmpty) {
return const SliverToBoxAdapter(child: Center(child: CircularProgressIndicator()));
}
return SliverPadding(
padding: const EdgeInsets.all(12.0),
sliver: SliverGrid.builder(
@@ -355,14 +355,14 @@ class SplashScreenPageState extends ConsumerState<SplashScreenPage> {
onError: (exception) => {
log.severe('Failed to update auth info with access token: $accessToken'),
ref.read(authProvider.notifier).logout(),
context.replaceRoute(const LoginRoute()),
context.router.replaceAll([const LoginRoute()]),
},
),
);
} else {
log.severe('Missing crucial offline login info - Logging out completely');
unawaited(ref.read(authProvider.notifier).logout());
unawaited(context.replaceRoute(const LoginRoute()));
unawaited(context.router.replaceAll([const LoginRoute()]));
return;
}
@@ -7,6 +7,7 @@ import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart
import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart';
@RoutePage()
@@ -34,11 +35,17 @@ class _DriftLockedFolderPageState extends ConsumerState<DriftLockedFolderPage> w
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (mounted) {
setState(() {
_showOverlay = state != AppLifecycleState.resumed;
});
if (!mounted) {
return;
}
if (state == AppLifecycleState.paused) {
ref.read(authProvider.notifier).lockPinCode();
context.navigateTo(const TabShellRoute());
return;
}
setState(() {
_showOverlay = state != AppLifecycleState.resumed;
});
}
@override
@@ -9,6 +9,7 @@ import 'package:immich_mobile/providers/auth.provider.dart';
import 'package:immich_mobile/providers/background_sync.provider.dart';
import 'package:immich_mobile/providers/backup/drift_backup.provider.dart';
import 'package:immich_mobile/providers/gallery_permission.provider.dart';
import 'package:immich_mobile/providers/infrastructure/memory.provider.dart';
import 'package:immich_mobile/providers/infrastructure/platform.provider.dart';
import 'package:immich_mobile/providers/infrastructure/settings.provider.dart';
import 'package:immich_mobile/providers/permission.provider.dart';
@@ -115,6 +116,7 @@ class AppLifeCycleNotifier extends StateNotifier<AppLifeCycleEnum> {
_safeRun(backgroundManager.syncLocal(full: CurrentPlatform.isAndroid ? true : false), "syncLocal"),
_safeRun(backgroundManager.syncRemote().then((success) => syncSuccess = success), "syncRemote"),
]);
_ref.invalidate(driftMemoryFutureProvider);
if (syncSuccess) {
await Future.wait([
_safeRun(backgroundManager.hashAssets(), "hashAssets").then((_) {
@@ -1,3 +1,5 @@
import 'dart:async';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/memory.model.dart';
import 'package:immich_mobile/domain/services/memory.service.dart';
@@ -19,6 +21,11 @@ final driftMemoryFutureProvider = FutureProvider.autoDispose<List<DriftMemory>>(
return const [];
}
final now = DateTime.now();
final nextMidnight = DateTime(now.year, now.month, now.day + 1);
final timer = Timer(nextMidnight.difference(now) + const Duration(seconds: 5), ref.invalidateSelf);
ref.onDispose(timer.cancel);
final service = ref.watch(driftMemoryServiceProvider);
return service.getMemoryLane(userId);
});
+1 -1
View File
@@ -3,7 +3,7 @@ Immich API
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
- API version: 3.0.0-rc.1
- API version: 3.0.0-rc.2
- Generator version: 7.22.0
- Build package: org.openapitools.codegen.languages.DartClientCodegen
+1 -1
View File
@@ -2,7 +2,7 @@ name: immich_mobile
description: Immich - selfhosted backup media file on mobile phone
publish_to: 'none'
version: 3.0.0-rc.1+3049
version: 3.0.0-rc.2+3050
environment:
sdk: '>=3.12.0 <4.0.0'
@@ -0,0 +1,96 @@
import 'package:fake_async/fake_async.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/domain/models/user.model.dart';
import 'package:immich_mobile/domain/services/memory.service.dart';
import 'package:immich_mobile/domain/services/user.service.dart';
import 'package:immich_mobile/providers/infrastructure/memory.provider.dart';
import 'package:immich_mobile/providers/user.provider.dart';
import 'package:mocktail/mocktail.dart';
class MockDriftMemoryService extends Mock implements DriftMemoryService {}
class MockUserService extends Mock implements UserService {}
void main() {
late MockDriftMemoryService memoryService;
late MockUserService userService;
UserDto user({bool memoryEnabled = true}) => UserDto(
id: 'user-1',
email: 'user@test.dev',
name: 'user',
memoryEnabled: memoryEnabled,
profileChangedAt: DateTime(2026),
);
ProviderContainer makeContainer() {
final container = ProviderContainer(
overrides: [
driftMemoryServiceProvider.overrideWithValue(memoryService),
currentUserProvider.overrideWith((ref) => CurrentUserProvider(userService)),
],
);
addTearDown(container.dispose);
return container;
}
setUp(() {
memoryService = MockDriftMemoryService();
userService = MockUserService();
when(() => memoryService.getMemoryLane('user-1')).thenAnswer((_) async => []);
when(() => userService.tryGetMyUser()).thenReturn(user());
when(() => userService.watchMyUser()).thenAnswer((_) => const Stream.empty());
});
group('driftMemoryFutureProvider', () {
test('re-queries after local midnight', () {
fakeAsync((async) {
final container = makeContainer();
container.listen(driftMemoryFutureProvider, (_, __) {});
async.flushMicrotasks();
verify(() => memoryService.getMemoryLane('user-1')).called(1);
async.elapse(const Duration(seconds: 4));
async.flushMicrotasks();
verifyNever(() => memoryService.getMemoryLane('user-1'));
async.elapse(const Duration(hours: 25));
async.flushMicrotasks();
verify(() => memoryService.getMemoryLane('user-1')).called(greaterThanOrEqualTo(1));
});
});
test('cancels the midnight timer when disposed', () {
fakeAsync((async) {
final container = makeContainer();
final subscription = container.listen(driftMemoryFutureProvider, (_, __) {});
async.flushMicrotasks();
verify(() => memoryService.getMemoryLane('user-1')).called(1);
subscription.close();
async.elapse(const Duration(hours: 25));
async.flushMicrotasks();
verifyNever(() => memoryService.getMemoryLane('user-1'));
});
});
test('does not query or arm the timer when memories are disabled', () {
when(() => userService.tryGetMyUser()).thenReturn(user(memoryEnabled: false));
fakeAsync((async) {
final container = makeContainer();
container.listen(driftMemoryFutureProvider, (_, __) {});
async.flushMicrotasks();
async.elapse(const Duration(hours: 25));
async.flushMicrotasks();
verifyNever(() => memoryService.getMemoryLane(any()));
});
});
});
}
+1 -1
View File
@@ -16206,7 +16206,7 @@
"info": {
"title": "Immich",
"description": "Immich API",
"version": "3.0.0-rc.1",
"version": "3.0.0-rc.2",
"contact": {}
},
"tags": [
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "immich-monorepo",
"version": "3.0.0-rc.1",
"version": "3.0.0-rc.2",
"description": "Monorepo for Immich",
"type": "module",
"private": true,
@@ -11,7 +11,7 @@
"release": "./misc/release/pump-version.sh",
"pump": "node ./misc/release/pump-wrapper.js"
},
"packageManager": "pnpm@11.5.2",
"packageManager": "pnpm@11.6.0",
"engines": {
"pnpm": ">=10.0.0"
},
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@immich/cli",
"version": "3.0.0-rc.1",
"version": "3.0.0-rc.2",
"description": "Command Line Interface (CLI) for Immich",
"repository": {
"type": "git",
+1 -1
View File
@@ -13,5 +13,5 @@
"oidc-provider": "^9.0.0",
"tsx": "^4.20.6"
},
"packageManager": "pnpm@11.5.2"
"packageManager": "pnpm@11.6.0"
}
+8 -8
View File
@@ -184,22 +184,22 @@
"uiHints": ["Filter"]
},
{
"name": "filterFileType",
"title": "Filter by file type",
"description": "Filter assets by file type",
"name": "assetTypeFilter",
"title": "Filter by asset type",
"description": "Filter assets by type",
"types": ["AssetV1"],
"schema": {
"type": "object",
"properties": {
"fileTypes": {
"title": "File types",
"description": "Allowed file types",
"allowedTypes": {
"title": "Asset types",
"description": "Allowed asset types",
"type": "string",
"array": true,
"enum": ["image", "video"]
"enum": ["IMAGE", "VIDEO", "AUDIO", "OTHER"]
}
},
"required": ["fileTypes"]
"required": ["allowedTypes"]
},
"uiHints": ["Filter"]
},
+1
View File
@@ -14,6 +14,7 @@ declare module 'main' {
export function assetFileFilter(): I32;
export function assetMissingTimeZoneFilter(): I32;
export function assetLocationFilter(): I32;
export function assetTypeFilter(): I32;
// updates
export function assetFavorite(): I32;
+7 -1
View File
@@ -1,5 +1,5 @@
import { wrapper } from '@immich/plugin-sdk';
import { AssetVisibility, WorkflowType } from '@immich/sdk';
import { AssetTypeEnum, AssetVisibility, WorkflowType } from '@immich/sdk';
type AssetFileFilterConfig = {
pattern: string;
@@ -95,6 +95,12 @@ export const assetLocationFilter = () => {
});
};
export const assetTypeFilter = () => {
return wrapper<WorkflowType.AssetV1, { allowedTypes: AssetTypeEnum[] }>(({ config, data }) => {
return { workflow: { continue: config.allowedTypes.includes(data.asset.type) } };
});
};
export const assetFavorite = () => {
return wrapper<WorkflowType.AssetV1, { inverse?: boolean }>(({ config, data }) => {
const target = config.inverse ? false : true;
+1 -1
View File
@@ -24,7 +24,7 @@
"keywords": [],
"author": "",
"license": "GNU Affero General Public License version 3",
"packageManager": "pnpm@11.5.2",
"packageManager": "pnpm@11.6.0",
"devDependencies": {
"@extism/js-pdk": "^1.1.1",
"@immich/sdk": "workspace:*",
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@immich/sdk",
"version": "3.0.0-rc.1",
"version": "3.0.0-rc.2",
"description": "Auto-generated TypeScript SDK for the Immich API",
"repository": {
"type": "git",
+1 -1
View File
@@ -1,6 +1,6 @@
/**
* Immich
* 3.0.0-rc.1
* 3.0.0-rc.2
* DO NOT MODIFY - This file has been generated using oazapfts.
* See https://www.npmjs.com/package/oazapfts
*/
+302 -335
View File
File diff suppressed because it is too large Load Diff
+8 -8
View File
@@ -1,6 +1,6 @@
{
"name": "immich",
"version": "3.0.0-rc.1",
"version": "3.0.0-rc.2",
"description": "",
"author": "",
"private": true,
@@ -49,14 +49,14 @@
"@nestjs/websockets": "^11.0.4",
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/context-async-hooks": "^2.0.0",
"@opentelemetry/exporter-prometheus": "^0.218.0",
"@opentelemetry/instrumentation-http": "^0.218.0",
"@opentelemetry/instrumentation-ioredis": "^0.66.0",
"@opentelemetry/instrumentation-nestjs-core": "^0.64.0",
"@opentelemetry/instrumentation-pg": "^0.70.0",
"@opentelemetry/exporter-prometheus": "^0.219.0",
"@opentelemetry/instrumentation-http": "^0.219.0",
"@opentelemetry/instrumentation-ioredis": "^0.67.0",
"@opentelemetry/instrumentation-nestjs-core": "^0.65.0",
"@opentelemetry/instrumentation-pg": "^0.71.0",
"@opentelemetry/resources": "^2.0.1",
"@opentelemetry/sdk-metrics": "^2.0.1",
"@opentelemetry/sdk-node": "^0.218.0",
"@opentelemetry/sdk-node": "^0.219.0",
"@opentelemetry/semantic-conventions": "^1.34.0",
"@react-email/components": "^1.0.0",
"@react-email/render": "^2.0.0",
@@ -95,7 +95,7 @@
"nestjs-kysely": "3.1.2",
"nestjs-otel": "^8.0.0",
"nestjs-zod": "^5.3.0",
"nodemailer": "^8.0.0",
"nodemailer": "^9.0.0",
"openid-client": "^6.3.3",
"pg": "^8.11.3",
"picomatch": "^4.0.2",
@@ -7,13 +7,14 @@ import {
IntegrityGetReportDto,
IntegrityReportResponseDto,
IntegrityReportSummaryResponseDto,
IntegrityReportTypeParamDto,
} from 'src/dtos/integrity.dto';
import { ApiTag, Permission } from 'src/enum';
import { Auth, Authenticated, FileResponse } from 'src/middleware/auth.guard';
import { LoggingRepository } from 'src/repositories/logging.repository';
import { IntegrityService } from 'src/services/integrity.service';
import { sendFile } from 'src/utils/file';
import { IntegrityReportTypeParamDto, UUIDv7ParamDto } from 'src/validation';
import { UUIDv7ParamDto } from 'src/validation';
@ApiTags(ApiTag.Maintenance)
@Controller('admin/integrity')
+7 -6
View File
@@ -10,8 +10,6 @@ const IntegrityReportSummaryResponseSchema = z
})
.meta({ id: 'IntegrityReportSummaryResponseDto' });
export class IntegrityReportSummaryResponseDto extends createZodDto(IntegrityReportSummaryResponseSchema) {}
const IntegrityGetReportSchema = z
.object({
type: IntegrityReportSchema,
@@ -20,17 +18,14 @@ const IntegrityGetReportSchema = z
})
.meta({ id: 'IntegrityGetReportDto' });
export class IntegrityGetReportDto extends createZodDto(IntegrityGetReportSchema) {}
const IntegrityDeleteReportSchema = z.object({ type: IntegrityReport }).meta({ id: 'IntegrityDeleteReportDto' });
export class IntegrityDeleteReportDto extends createZodDto(IntegrityDeleteReportSchema) {}
const IntegrityReportResponseItemSchema = z.object({
id: z.uuidv4().describe('Integrity report item id'),
type: IntegrityReportSchema,
path: z.string().describe('Integrity report item path'),
});
const IntegrityReportResponseSchema = z
.object({
items: z.array(IntegrityReportResponseItemSchema),
@@ -38,4 +33,10 @@ const IntegrityReportResponseSchema = z
})
.meta({ id: 'IntegrityReportResponseDto' });
const IntegrityReportParamSchema = z.object({ type: IntegrityReportSchema }).meta({ id: 'IntegrityReportDto' });
export class IntegrityReportSummaryResponseDto extends createZodDto(IntegrityReportSummaryResponseSchema) {}
export class IntegrityGetReportDto extends createZodDto(IntegrityGetReportSchema) {}
export class IntegrityDeleteReportDto extends createZodDto(IntegrityDeleteReportSchema) {}
export class IntegrityReportResponseDto extends createZodDto(IntegrityReportResponseSchema) {}
export class IntegrityReportTypeParamDto extends createZodDto(IntegrityReportParamSchema) {}
@@ -69,7 +69,16 @@ export class ServerInfoRepository {
try {
const { versionCheck } = this.configRepository.getEnv();
const url = new URL(versionCheck.url);
url.searchParams.append('channel', channel);
switch (channel) {
case ReleaseChannel.Stable: {
url.searchParams.append('channel', 'stable');
break;
}
case ReleaseChannel.ReleaseCandidate: {
url.searchParams.append('channel', 'rc');
break;
}
}
const response = await fetch(url);
if (!response.ok) {
@@ -26,6 +26,7 @@ export async function up(db: Kysely<any>): Promise<void> {
.selectFrom('album')
.select(['album.id as albumId', 'album.ownerId as userId', eb.val(AlbumUserRole.Owner).as('role')]),
)
.onConflict((cb) => cb.columns(['albumId', 'userId']).doUpdateSet({ role: AlbumUserRole.Owner }))
.execute();
await sql`ALTER TABLE "album" DROP CONSTRAINT "album_ownerId_fkey";`.execute(db);
await sql`ALTER TABLE "album" DROP COLUMN "ownerId";`.execute(db);
-5
View File
@@ -1,7 +1,6 @@
import { ArgumentMetadata, FileValidator, Injectable, ParseUUIDPipe } from '@nestjs/common';
import { createZodDto } from 'nestjs-zod';
import sanitize from 'sanitize-filename';
import { IntegrityReportSchema } from 'src/enum';
import { isIP, isIPRange } from 'validator';
import z from 'zod';
@@ -132,10 +131,6 @@ const FilenameParamSchema = z.object({
export class FilenameParamDto extends createZodDto(FilenameParamSchema) {}
const IntegrityReportParamSchema = z.object({ type: IntegrityReportSchema }).meta({ id: 'IntegrityReportDto' });
export class IntegrityReportTypeParamDto extends createZodDto(IntegrityReportParamSchema) {}
/**
* Unified email validation
* Converts email strings to lowercase and validates against HTML5 email regex
@@ -2,7 +2,7 @@ import { WorkflowStepConfig, WorkflowTrigger } from '@immich/plugin-sdk';
import { Kysely } from 'kysely';
import { readFileSync } from 'node:fs';
import { PluginManifestDto } from 'src/dtos/plugin-manifest.dto';
import { AssetVisibility, LogLevel } from 'src/enum';
import { AssetType, AssetVisibility, LogLevel } from 'src/enum';
import { AccessRepository } from 'src/repositories/access.repository';
import { AlbumRepository } from 'src/repositories/album.repository';
import { AssetRepository } from 'src/repositories/asset.repository';
@@ -403,4 +403,28 @@ describe('core plugin', () => {
await expect(ctx.get(AssetRepository).getById(asset.id)).resolves.toMatchObject({ isFavorite: true });
});
});
describe('assetTypeFilter', () => {
it('should favorite asset if it is a video', async () => {
const { user } = await ctx.newUser();
const { asset } = await ctx.newAsset({ ownerId: user.id, type: AssetType.Video });
const workflow = await createWorkflow({
ownerId: user.id,
trigger: WorkflowTrigger.AssetCreate,
steps: [
{
method: 'immich-plugin-core#assetTypeFilter',
config: { allowedTypes: ['VIDEO'] },
},
{
method: 'immich-plugin-core#assetFavorite',
},
],
});
await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined();
await expect(ctx.get(AssetRepository).getById(asset.id)).resolves.toMatchObject({ isFavorite: true });
});
});
});
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "immich-web",
"version": "3.0.0-rc.1",
"version": "3.0.0-rc.2",
"license": "GNU Affero General Public License version 3",
"type": "module",
"scripts": {
@@ -105,7 +105,7 @@
"prettier-plugin-sort-json": "^4.1.1",
"prettier-plugin-svelte": "^4.0.0",
"rollup-plugin-visualizer": "^7.0.0",
"svelte": "5.56.2",
"svelte": "5.56.3",
"svelte-check": "^4.4.6",
"svelte-eslint-parser": "^1.3.3",
"tailwindcss": "^4.2.4",
@@ -499,11 +499,9 @@
{album}
{person}
{stack}
showSlideshow={true}
preAction={handlePreAction}
onAction={handleAction}
{onUndoDelete}
onPlaySlideshow={() => ($slideshowState = SlideshowState.PlaySlideshow)}
onClose={onClose ? () => onClose(stack?.primaryAssetId ?? asset.id) : undefined}
{onRemoveFromAlbum}
{playOriginalVideo}
@@ -9,7 +9,6 @@
import RatingAction from '$lib/components/asset-viewer/actions/RatingAction.svelte';
import RemoveAssetFromStack from '$lib/components/asset-viewer/actions/RemoveAssetFromStack.svelte';
import RestoreAction from '$lib/components/asset-viewer/actions/RestoreAction.svelte';
import SetAlbumCoverAction from '$lib/components/asset-viewer/actions/SetAlbumCoverAction.svelte';
import SetFeaturedPhotoAction from '$lib/components/asset-viewer/actions/SetPersonFeaturedAction.svelte';
import SetProfilePictureAction from '$lib/components/asset-viewer/actions/SetProfilePictureAction.svelte';
import SetStackPrimaryAsset from '$lib/components/asset-viewer/actions/SetStackPrimaryAsset.svelte';
@@ -24,6 +23,7 @@
import { featureFlagsManager } from '$lib/managers/feature-flags-manager.svelte';
import { languageManager } from '$lib/managers/language-manager.svelte';
import { Route } from '$lib/route';
import { getAlbumAssetActions } from '$lib/services/album.service';
import { getGlobalActions } from '$lib/services/app.service';
import { getAssetActions } from '$lib/services/asset.service';
import { getSharedLink, withoutIcons } from '$lib/utils';
@@ -38,15 +38,7 @@
type StackResponseDto,
} from '@immich/sdk';
import { ActionButton, CommandPaletteDefaultProvider, Tooltip, type ActionItem } from '@immich/ui';
import {
mdiArrowLeft,
mdiArrowRight,
mdiCompare,
mdiDotsVertical,
mdiImageSearch,
mdiPresentationPlay,
mdiVideoOutline,
} from '@mdi/js';
import { mdiArrowLeft, mdiArrowRight, mdiCompare, mdiDotsVertical, mdiImageSearch, mdiVideoOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
interface Props {
@@ -54,11 +46,9 @@
album?: AlbumResponseDto | null;
person?: PersonResponseDto | null;
stack?: StackResponseDto | null;
showSlideshow?: boolean;
preAction: PreAction;
onAction: OnAction;
onUndoDelete?: OnUndoDelete;
onPlaySlideshow: () => void;
onClose?: () => void;
onRemoveFromAlbum?: (assetIds: string[]) => void;
playOriginalVideo: boolean;
@@ -70,11 +60,9 @@
album = null,
person = null,
stack = null,
showSlideshow = false,
preAction,
onAction,
onUndoDelete = undefined,
onPlaySlideshow,
onClose,
onRemoveFromAlbum,
playOriginalVideo = false,
@@ -147,9 +135,7 @@
{#if !sharedLink}
<ButtonContextMenu direction="left" align="top-right" color="secondary" title={$t('more')} icon={mdiDotsVertical}>
{#if showSlideshow && !isLocked}
<MenuOption icon={mdiPresentationPlay} text={$t('slideshow')} onClick={onPlaySlideshow} />
{/if}
<ActionMenuItem action={Actions.PlaySlideshow} />
<ActionMenuItem action={Actions.Download} />
<ActionMenuItem action={Actions.DownloadOriginal} />
@@ -177,7 +163,8 @@
{/if}
{/if}
{#if album}
<SetAlbumCoverAction {asset} {album} />
{@const { SetCover } = getAlbumAssetActions($t, album, asset)}
<ActionMenuItem action={SetCover} />
{/if}
{#if person}
<SetFeaturedPhotoAction {asset} {person} {onAction} />
@@ -1,33 +0,0 @@
<script lang="ts">
import MenuOption from '$lib/components/shared-components/context-menu/MenuOption.svelte';
import { eventManager } from '$lib/managers/event-manager.svelte';
import { handleError } from '$lib/utils/handle-error';
import { updateAlbumInfo, type AlbumResponseDto, type AssetResponseDto } from '@immich/sdk';
import { toastManager } from '@immich/ui';
import { mdiImageOutline } from '@mdi/js';
import { t } from 'svelte-i18n';
interface Props {
asset: AssetResponseDto;
album: AlbumResponseDto;
}
let { asset, album }: Props = $props();
const handleUpdateThumbnail = async () => {
try {
const response = await updateAlbumInfo({
id: album.id,
updateAlbumDto: {
albumThumbnailAssetId: asset.id,
},
});
eventManager.emit('AlbumUpdate', response);
toastManager.primary($t('album_cover_updated'));
} catch (error) {
handleError(error, $t('errors.unable_to_update_album_cover'));
}
};
</script>
<MenuOption text={$t('set_as_album_cover')} icon={mdiImageOutline} onClick={handleUpdateThumbnail} />
@@ -44,7 +44,7 @@
brokenAssetClass?: ClassValue;
dimmed?: boolean;
albumUsers?: UserResponseDto[];
onClick?: (asset: TimelineAsset, event?: MouseEvent) => void;
onClick?: (asset: TimelineAsset) => void;
onPreview?: (asset: TimelineAsset) => void;
onSelect?: (asset: TimelineAsset) => void;
onMouseEvent?: (event: { isMouseOver: boolean; selectedGroupIndex: number }) => void;
@@ -93,12 +93,12 @@
}
};
const callClickHandlers = (e?: MouseEvent) => {
const callClickHandlers = () => {
if (selected) {
onIconClickedHandler(e);
onIconClickedHandler();
return;
}
onClick?.($state.snapshot(asset), e);
onClick?.($state.snapshot(asset));
};
const handleClick = (e: MouseEvent) => {
@@ -109,7 +109,7 @@
e.stopPropagation();
e.preventDefault();
callClickHandlers(e);
callClickHandlers();
};
const onMouseEnter = () => {
@@ -13,6 +13,7 @@
import AssetDeleteConfirmModal from '$lib/modals/AssetDeleteConfirmModal.svelte';
import ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
import { Route } from '$lib/route';
import { keyboardManager } from '$lib/stores/keyboard-manager.svelte';
import { showDeleteModal } from '$lib/stores/preferences.store';
import { handlePromiseError } from '$lib/utils';
import { deleteAssets } from '$lib/utils/actions';
@@ -85,7 +86,6 @@
return top + pageHeaderOffset < window.bottom && top + geometry.getHeight(index) > window.top;
};
let shiftKeyIsDown = $state(false);
let lastAssetMouseEvent: TimelineAsset | null = $state(null);
let scrollTop = $state(0);
@@ -122,21 +122,6 @@
assetInteraction.selectAssets(assets.map((a) => toTimelineAsset(a)));
};
const onKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Shift') {
event.preventDefault();
shiftKeyIsDown = true;
}
};
const onKeyUp = (event: KeyboardEvent) => {
if (event.key === 'Shift') {
event.preventDefault();
shiftKeyIsDown = false;
}
};
const handleSelectAssets = (asset: TimelineAsset) => {
if (!asset) {
return;
@@ -168,7 +153,7 @@
};
const selectAssetCandidates = (endAsset: TimelineAsset) => {
if (!shiftKeyIsDown) {
if (!keyboardManager.shift) {
return;
}
@@ -188,7 +173,7 @@
};
const onSelectStart = (event: Event) => {
if (assetInteraction.selectionActive && shiftKeyIsDown) {
if (assetInteraction.selectionActive && keyboardManager.shift) {
event.preventDefault();
}
};
@@ -333,13 +318,13 @@
});
$effect(() => {
if (!shiftKeyIsDown) {
if (!keyboardManager.shift) {
assetInteraction.clearCandidates();
}
});
$effect(() => {
if (shiftKeyIsDown && lastAssetMouseEvent) {
if (keyboardManager.shift && lastAssetMouseEvent) {
selectAssetCandidates(lastAssetMouseEvent);
}
});
@@ -351,13 +336,7 @@
});
</script>
<svelte:document
onkeydown={onKeyDown}
onkeyup={onKeyUp}
onselectstart={onSelectStart}
use:shortcuts={shortcutList}
onscroll={() => updateSlidingWindow()}
/>
<svelte:document onselectstart={onSelectStart} use:shortcuts={shortcutList} onscroll={() => updateSlidingWindow()} />
{#if assets.length > 0}
<div
@@ -19,6 +19,7 @@
import { TimelineManager } from '$lib/managers/timeline-manager/timeline-manager.svelte';
import type { TimelineAsset, TimelineManagerOptions, ViewportTopMonth } from '$lib/managers/timeline-manager/types';
import { assetsSnapshot } from '$lib/managers/timeline-manager/utils.svelte';
import { keyboardManager } from '$lib/stores/keyboard-manager.svelte';
import { mediaQueryManager } from '$lib/stores/media-query-manager.svelte';
import { isAssetViewerRoute, navigate } from '$lib/utils/navigation';
import { getTimes, type ScrubberListener } from '$lib/utils/timeline-util';
@@ -59,7 +60,6 @@
groupTitle: string,
asset: TimelineAsset,
) => void,
event?: MouseEvent,
) => void;
}
@@ -371,21 +371,6 @@
let lastAssetMouseEvent: TimelineAsset | null = $state(null);
let shiftKeyIsDown = $state(false);
const onKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Shift') {
event.preventDefault();
shiftKeyIsDown = true;
}
};
const onKeyUp = (event: KeyboardEvent) => {
if (event.key === 'Shift') {
event.preventDefault();
shiftKeyIsDown = false;
}
};
const handleSelectAssetCandidates = (asset: TimelineAsset | null) => {
if (asset) {
void selectAssetCandidates(asset);
@@ -486,7 +471,7 @@
};
const selectAssetCandidates = async (endAsset: TimelineAsset) => {
if (!shiftKeyIsDown) {
if (!keyboardManager.shift) {
return;
}
@@ -506,13 +491,13 @@
});
$effect(() => {
if (!shiftKeyIsDown) {
if (!keyboardManager.shift) {
assetInteraction.clearCandidates();
}
});
$effect(() => {
if (shiftKeyIsDown && lastAssetMouseEvent) {
if (keyboardManager.shift && lastAssetMouseEvent) {
void selectAssetCandidates(lastAssetMouseEvent);
}
});
@@ -561,8 +546,6 @@
};
</script>
<svelte:document onkeydown={onKeyDown} onkeyup={onKeyUp} />
<HotModuleReload
onAfterUpdate={() => {
const asset = page.url.searchParams.get('at');
@@ -600,12 +583,12 @@
onScrubKeyDown={(evt) => {
evt.preventDefault();
let amount = 50;
if (shiftKeyIsDown) {
if (keyboardManager.shift) {
amount = 500;
}
if (evt.key === 'ArrowUp') {
amount = -amount;
if (shiftKeyIsDown) {
if (keyboardManager.shift) {
scrollableElement?.scrollBy({ top: amount, behavior: 'smooth' });
}
} else if (evt.key === 'ArrowDown') {
@@ -686,9 +669,9 @@
{asset}
{albumUsers}
{groupIndex}
onClick={(asset, event) => {
onClick={(asset) => {
if (typeof onThumbnailClick === 'function') {
onThumbnailClick(asset, timelineManager, timelineDay, _onClick, event);
onThumbnailClick(asset, timelineManager, timelineDay, _onClick);
} else {
_onClick(timelineManager, timelineDay.getAssets(), timelineDay.groupTitle, asset);
}
@@ -15,6 +15,7 @@
import NavigateToDateModal from '$lib/modals/NavigateToDateModal.svelte';
import ShortcutsModal from '$lib/modals/ShortcutsModal.svelte';
import { Route } from '$lib/route';
import { keyboardManager } from '$lib/stores/keyboard-manager.svelte';
import { showDeleteModal } from '$lib/stores/preferences.store';
import { searchStore } from '$lib/stores/search.svelte';
import { handlePromiseError } from '$lib/utils';
@@ -73,32 +74,8 @@
assetInteraction.clear();
};
let shiftKeyIsDown = $state(false);
const onKeyDown = (event: KeyboardEvent) => {
if (searchStore.isSearchEnabled) {
return;
}
if (event.key === 'Shift') {
event.preventDefault();
shiftKeyIsDown = true;
}
};
const onKeyUp = (event: KeyboardEvent) => {
if (searchStore.isSearchEnabled) {
return;
}
if (event.key === 'Shift') {
event.preventDefault();
shiftKeyIsDown = false;
}
};
const onSelectStart = (e: Event) => {
if (assetInteraction.selectionActive && shiftKeyIsDown) {
if (!searchStore.isSearchEnabled && assetInteraction.selectionActive && keyboardManager.shift) {
e.preventDefault();
}
};
@@ -171,4 +148,4 @@
});
</script>
<svelte:document onkeydown={onKeyDown} onkeyup={onKeyUp} onselectstart={onSelectStart} use:shortcuts={shortcutList} />
<svelte:document onselectstart={onSelectStart} use:shortcuts={shortcutList} />
+29 -1
View File
@@ -10,12 +10,13 @@ import {
updateAlbumUser,
type AlbumResponseDto,
type AlbumsAddAssetsResponseDto,
type AssetResponseDto,
type BulkIdResponseDto,
type UpdateAlbumDto,
type UserResponseDto,
} from '@immich/sdk';
import { modalManager, toastManager, type ActionItem } from '@immich/ui';
import { mdiLink, mdiPlus, mdiPlusBoxOutline, mdiShareVariantOutline, mdiUpload } from '@mdi/js';
import { mdiImageOutline, mdiLink, mdiPlus, mdiPlusBoxOutline, mdiShareVariantOutline, mdiUpload } from '@mdi/js';
import { type MessageFormatter } from 'svelte-i18n';
import { goto } from '$app/navigation';
import { authManager } from '$lib/managers/auth-manager.svelte';
@@ -68,6 +69,16 @@ export const getAlbumActions = ($t: MessageFormatter, album: AlbumResponseDto) =
return { Share, AddUsers, CreateSharedLink };
};
export const getAlbumAssetActions = ($t: MessageFormatter, album: AlbumResponseDto, asset: AssetResponseDto) => {
const SetCover: ActionItem = {
title: $t('set_as_album_cover'),
icon: mdiImageOutline,
onAction: () => handleUpdateThumbnail(album, asset.id),
};
return { SetCover };
};
export const getAlbumAssetsActions = ($t: MessageFormatter, album: AlbumResponseDto, assets: TimelineAsset[]) => {
const AddAssets: ActionItem = {
title: $t('add_assets'),
@@ -206,6 +217,23 @@ export const handleRemoveUserFromAlbum = async (album: AlbumResponseDto, albumUs
}
};
const handleUpdateThumbnail = async (album: AlbumResponseDto, assetId: string) => {
const $t = await getFormatter();
try {
const response = await updateAlbumInfo({
id: album.id,
updateAlbumDto: {
albumThumbnailAssetId: assetId,
},
});
eventManager.emit('AlbumUpdate', response);
toastManager.primary($t('album_cover_updated'));
} catch (error) {
handleError(error, $t('errors.unable_to_update_album_cover'));
}
};
export const handleUpdateAlbum = async ({ id }: { id: string }, dto: UpdateAlbumDto) => {
const $t = await getFormatter();
+10
View File
@@ -28,6 +28,7 @@ import {
mdiMotionPauseOutline,
mdiMotionPlayOutline,
mdiPlus,
mdiPresentationPlay,
mdiShareVariantOutline,
mdiTagPlusOutline,
mdiTune,
@@ -41,6 +42,7 @@ import { eventManager } from '$lib/managers/event-manager.svelte';
import AssetAddToAlbumModal from '$lib/modals/AssetAddToAlbumModal.svelte';
import AssetTagModal from '$lib/modals/AssetTagModal.svelte';
import SharedLinkCreateModal from '$lib/modals/SharedLinkCreateModal.svelte';
import { SlideshowState, slideshowStore } from '$lib/stores/slideshow.store';
import { getAssetMediaUrl, getSharedLink, sleep } from '$lib/utils';
import { downloadUrl } from '$lib/utils';
import { handleError } from '$lib/utils/handle-error';
@@ -140,6 +142,13 @@ export const getAssetActions = ($t: MessageFormatter, asset: AssetResponseDto) =
},
};
const PlaySlideshow: ActionItem = {
title: $t('slideshow'),
icon: mdiPresentationPlay,
$if: () => asset.visibility !== AssetVisibility.Locked,
onAction: () => slideshowStore.slideshowState.set(SlideshowState.PlaySlideshow),
};
const Favorite: ActionItem = {
title: $t('to_favorite'),
icon: mdiHeartOutline,
@@ -269,6 +278,7 @@ export const getAssetActions = ($t: MessageFormatter, asset: AssetResponseDto) =
Unfavorite,
PlayMotionPhoto,
StopMotionPhoto,
PlaySlideshow,
AddToAlbum,
ZoomIn,
ZoomOut,
+3 -2
View File
@@ -24,7 +24,8 @@ class FaceManager {
});
readonly people = $derived.by(() => {
const people = new SvelteMap<string, PersonResponseDto>();
// eslint-disable-next-line svelte/prefer-svelte-reactivity
const people = new Map<string, PersonResponseDto>();
for (const face of this.data) {
if (face.person) {
@@ -32,7 +33,7 @@ class FaceManager {
}
}
return people.values();
return Array.from(people.values());
});
readonly facesByPersonId = $derived.by(() => {
@@ -0,0 +1,47 @@
class KeyboardManager {
#shift = $state(false);
#ctrl = $state(false);
#meta = $state(false);
#alt = $state(false);
constructor() {
if (globalThis.window === undefined) {
return;
}
globalThis.addEventListener('keydown', this.#update);
globalThis.addEventListener('keyup', this.#update);
globalThis.addEventListener('blur', this.#clear);
}
get shift() {
return this.#shift;
}
get ctrl() {
return this.#ctrl;
}
get meta() {
return this.#meta;
}
get alt() {
return this.#alt;
}
#update = (event: KeyboardEvent) => {
this.#shift = event.shiftKey;
this.#ctrl = event.ctrlKey;
this.#meta = event.metaKey;
this.#alt = event.altKey;
};
#clear = () => {
this.#shift = false;
this.#ctrl = false;
this.#meta = false;
this.#alt = false;
};
}
export const keyboardManager = new KeyboardManager();
@@ -11,6 +11,7 @@
import type { TimelineAsset } from '$lib/managers/timeline-manager/types';
import GeolocationPointPickerModal from '$lib/modals/GeolocationPointPickerModal.svelte';
import GeolocationUpdateConfirmModal from '$lib/modals/GeolocationUpdateConfirmModal.svelte';
import { keyboardManager } from '$lib/stores/keyboard-manager.svelte';
import type { LatLng } from '$lib/types';
import { setQueryValue } from '$lib/utils/navigation';
import { toTimelineAsset } from '$lib/utils/timeline-util';
@@ -118,13 +119,10 @@
groupTitle: string,
asset: TimelineAsset,
) => void,
event?: MouseEvent,
) => {
if (event?.shiftKey) {
if (keyboardManager.shift) {
onClick(timelineManager, timelineDay.getAssets(), timelineDay.groupTitle, asset);
return;
}
if (hasGps(asset)) {
} else if (hasGps(asset)) {
locationUpdated = true;
setTimeout(() => {
locationUpdated = false;
@@ -73,7 +73,13 @@
let dragSourceId: string | undefined;
const workflowSummary = $derived({ name, description, trigger, steps });
const workflowJsonContent = $derived<WorkflowJsonContent>({ name, description, enabled, trigger, steps });
const workflowJsonContent = $derived<WorkflowJsonContent>({
name,
description,
enabled,
trigger,
steps: steps.map(({ id: _, ...step }) => step),
});
const hasChanges = $derived(
enabled !== savedWorkflow.enabled ||