Compare commits

..

24 Commits

Author SHA1 Message Date
github-actions
f5ff36a1f8 chore: version v2.2.2 2025-11-02 21:56:36 +00:00
Alex
b5efc9c16e fix: passing secrets to trigger workflow (#23447)
* fix: passing secrets to trigger workflow

* pass secrets to workflow call
2025-11-02 15:54:35 -06:00
Alex
1036076b0d fix: disable prunning for more investigation (#23531) 2025-11-02 15:54:03 -06:00
Daniel Dietzler
c76324c611 fix(web): mobile scrubber on page load (#23488) 2025-11-01 22:15:33 -05:00
bo0tzz
0ddb92e1ec fix: use pnpm directly for fix-format (#23483) 2025-11-01 15:38:18 -04:00
Alex
d08a520aa2 chore: post release tasks (#23443) 2025-11-01 01:21:39 -05:00
dotlambda
7bdf0f6c50 chore(ml): remove setuptools from dependencies (#23446) 2025-10-31 21:34:10 +00:00
shenlong
2b33a58448 fix: show in timeline from search page (#23440)
Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2025-10-31 14:55:28 -05:00
github-actions
b35f00f768 chore: version v2.2.1 2025-10-31 18:04:27 +00:00
Weblate (bot)
86cc7c3c73 chore(web): update translations (#23375)
Translate-URL: https://hosted.weblate.org/projects/immich/immich/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/da/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/de/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/fr/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/hi/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/nb_NO/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/pl/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/pt/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ru/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/sv/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/ta/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/tr/
Translate-URL: https://hosted.weblate.org/projects/immich/immich/zh_Hant/
Translation: Immich/immich

Co-authored-by: Akhil Raj Baranwal <akhil.r.baranwal@gmail.com>
Co-authored-by: Dennis Kjær Jensen <weblate@signout.dk>
Co-authored-by: DevServs <bonov@mail.ru>
Co-authored-by: Florian Amsallem <florian.amsallem@gmail.com>
Co-authored-by: Hurricane-32 <rodrigorimo@hotmail.com>
Co-authored-by: Kai Heine <kai-heine@users.noreply.hosted.weblate.org>
Co-authored-by: Marrick Schröder <marrick.schroeder@gmail.com>
Co-authored-by: Michael <parieren.gefuehl5g@icloud.com>
Co-authored-by: PontusÖsterlindh <pontus@osterlindh.com>
Co-authored-by: S M, Aravinth (A.) <asm1@ford.com>
Co-authored-by: User 123456789 <user123456789@users.noreply.hosted.weblate.org>
Co-authored-by: Vegard Fladby <vegard@fladby.org>
Co-authored-by: linux-universe <lauro@dilorenzo.one>
Co-authored-by: shiuh67 <shiuh.cheng@gmail.com>
Co-authored-by: slick-daddy <129640104+slick-daddy@users.noreply.github.com>
Co-authored-by: ti-guru <anders.egeland@outlook.com>
2025-10-31 18:02:30 +00:00
Alex
5854cbbe97 fix: show close button on purchase modal (#23436) 2025-10-31 17:47:14 +00:00
Alex
ceb36a304d fix: view in timeline does not jump to the timeline correctly (#23428) 2025-10-31 17:24:41 +00:00
Daniel Dietzler
f5d7e5acca chore: cannonical tailwind classes (#23427) 2025-10-31 11:38:17 -04:00
luneth
be15a84f9b chore: update android signing fingerprints to docs (#23361)
* Update mobile-app.mdx

Add certificate fingerprint for android releases.

* chore: formatting

* Chore: Typo

---------

Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2025-10-31 09:40:53 -05:00
Alex
32791e98c2 chore: trigger prod build on prepare-release (#23424)
* chore: trigger prod build on prepare-release

* clean up
2025-10-31 14:26:03 +00:00
Alex
7ea443b3a9 chore: gha ios release | take 5 (#23203)
* chore: gha ios release | take 5

* code signing

* code signing 2

* manual signing for extensions

* chore(ios): add explicit code signing identity and debug output

* dev appbundle

* Deployment flow for development app

* skip waiting for change log

* refactor

* fix: ruby version

* fix: manual release lane

* build on main
2025-10-31 09:05:03 -05:00
Alex
c69786b039 fix: button condition rendering (#23400) 2025-10-31 08:42:01 -05:00
Mert
5c7d5539ea fix(mobile): video seeking on android (#23405)
use int for seeking
2025-10-31 08:41:09 -05:00
Daniel Dietzler
3531856d1c refactor: api key modals (#23420) 2025-10-31 08:58:52 -04:00
Mert
4abaad548a fix(ml): ocr failing with rootless docker (#23402)
don't download font
2025-10-31 02:41:49 -04:00
Jonathan Jogenfors
61a2c3ace3 chore(server): clarify asset copy parameters (#23396) 2025-10-30 23:55:39 +00:00
Daniel Dietzler
e9038193db fix: asset copy validation error (#23387) 2025-10-30 19:40:58 -04:00
bo0tzz
3f5cd48a59 fix: don't use app token for cli push (#23378) 2025-10-30 21:31:56 +01:00
idubnori
4cb094e7ae fix(mobile): regression - not displayed activity button in top bar (#23366) 2025-10-30 14:39:36 -05:00
124 changed files with 920 additions and 495 deletions

View File

@@ -1,12 +1,16 @@
name: Build Mobile
on:
workflow_dispatch:
workflow_call:
inputs:
ref:
required: false
type: string
environment:
description: 'Target environment'
required: true
default: 'development'
type: string
secrets:
KEY_JKS:
required: true
@@ -16,6 +20,30 @@ on:
required: true
ANDROID_STORE_PASSWORD:
required: true
APP_STORE_CONNECT_API_KEY_ID:
required: true
APP_STORE_CONNECT_API_KEY_ISSUER_ID:
required: true
APP_STORE_CONNECT_API_KEY:
required: true
IOS_CERTIFICATE_P12:
required: true
IOS_CERTIFICATE_PASSWORD:
required: true
IOS_PROVISIONING_PROFILE:
required: true
IOS_PROVISIONING_PROFILE_SHARE_EXTENSION:
required: true
IOS_PROVISIONING_PROFILE_WIDGET_EXTENSION:
required: true
IOS_DEVELOPMENT_PROVISIONING_PROFILE:
required: true
IOS_DEVELOPMENT_PROVISIONING_PROFILE_SHARE_EXTENSION:
required: true
IOS_DEVELOPMENT_PROVISIONING_PROFILE_WIDGET_EXTENSION:
required: true
FASTLANE_TEAM_ID:
required: true
pull_request:
push:
branches: [main]
@@ -193,17 +221,22 @@ jobs:
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.4.7'
ruby-version: '3.3'
working-directory: ./mobile/ios
- name: Install Fastlane
- name: Install CocoaPods dependencies
working-directory: ./mobile/ios
run: |
pod install
- name: Install Fastlane
working-directory: ./mobile/ios
run: |
cd mobile/ios
gem install bundler
bundle config set --local path 'vendor/bundle'
bundle install
- name: Create API Key JSON
- name: Create API Key
env:
API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
@@ -212,35 +245,55 @@ jobs:
run: |
mkdir -p ~/.appstoreconnect/private_keys
echo "$API_KEY_CONTENT" | base64 --decode > ~/.appstoreconnect/private_keys/AuthKey_${API_KEY_ID}.p8
cat > api_key.json << EOF
{
"key_id": "${API_KEY_ID}",
"issuer_id": "${API_KEY_ISSUER_ID}",
"key": "$(cat ~/.appstoreconnect/private_keys/AuthKey_${API_KEY_ID}.p8)",
"duration": 1200,
"in_house": false
}
EOF
- name: Import Certificate and Provisioning Profile
- name: Import Certificate and Provisioning Profiles
env:
IOS_CERTIFICATE_P12: ${{ secrets.IOS_CERTIFICATE_P12 }}
IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
IOS_PROVISIONING_PROFILE: ${{ secrets.IOS_PROVISIONING_PROFILE }}
IOS_PROVISIONING_PROFILE_SHARE_EXTENSION: ${{ secrets.IOS_PROVISIONING_PROFILE_SHARE_EXTENSION }}
IOS_PROVISIONING_PROFILE_WIDGET_EXTENSION: ${{ secrets.IOS_PROVISIONING_PROFILE_WIDGET_EXTENSION }}
IOS_DEVELOPMENT_PROVISIONING_PROFILE: ${{ secrets.IOS_DEVELOPMENT_PROVISIONING_PROFILE }}
IOS_DEVELOPMENT_PROVISIONING_PROFILE_SHARE_EXTENSION: ${{ secrets.IOS_DEVELOPMENT_PROVISIONING_PROFILE_SHARE_EXTENSION }}
IOS_DEVELOPMENT_PROVISIONING_PROFILE_WIDGET_EXTENSION: ${{ secrets.IOS_DEVELOPMENT_PROVISIONING_PROFILE_WIDGET_EXTENSION }}
ENVIRONMENT: ${{ inputs.environment || 'development' }}
working-directory: ./mobile/ios
run: |
# Decode certificate
echo "$IOS_CERTIFICATE_P12" | base64 --decode > certificate.p12
echo "$IOS_PROVISIONING_PROFILE" | base64 --decode > profile.mobileprovision
- name: Create keychain
# Decode provisioning profiles based on environment
if [[ "$ENVIRONMENT" == "development" ]]; then
echo "$IOS_DEVELOPMENT_PROVISIONING_PROFILE" | base64 --decode > profile_dev.mobileprovision
echo "$IOS_DEVELOPMENT_PROVISIONING_PROFILE_SHARE_EXTENSION" | base64 --decode > profile_dev_share.mobileprovision
echo "$IOS_DEVELOPMENT_PROVISIONING_PROFILE_WIDGET_EXTENSION" | base64 --decode > profile_dev_widget.mobileprovision
ls -lh profile_dev*.mobileprovision
else
echo "$IOS_PROVISIONING_PROFILE" | base64 --decode > profile.mobileprovision
echo "$IOS_PROVISIONING_PROFILE_SHARE_EXTENSION" | base64 --decode > profile_share.mobileprovision
echo "$IOS_PROVISIONING_PROFILE_WIDGET_EXTENSION" | base64 --decode > profile_widget.mobileprovision
ls -lh profile*.mobileprovision
fi
- name: Create keychain and import certificate
env:
KEYCHAIN_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
working-directory: ./mobile/ios
run: |
# Create keychain
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -t 3600 -u build.keychain
# Import certificate
security import certificate.p12 -k build.keychain -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" build.keychain
# Verify certificate was imported
security find-identity -v -p codesigning build.keychain
- name: Build and deploy to TestFlight
env:
FASTLANE_TEAM_ID: ${{ secrets.FASTLANE_TEAM_ID }}
@@ -249,8 +302,14 @@ jobs:
KEYCHAIN_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
ENVIRONMENT: ${{ inputs.environment || 'development' }}
working-directory: ./mobile/ios
run: bundle exec fastlane release_ci
run: |
if [[ "$ENVIRONMENT" == "development" ]]; then
bundle exec fastlane gha_testflight_dev
else
bundle exec fastlane gha_release_prod
fi
- name: Clean up keychain
if: always()

View File

@@ -95,7 +95,7 @@ jobs:
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ steps.token.outputs.token }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Get package version
id: package-version
@@ -125,4 +125,3 @@ jobs:
cache-to: type=gha,mode=max
tags: ${{ steps.metadata.outputs.tags }}
labels: ${{ steps.metadata.outputs.labels }}
github-token: ${{ steps.token.outputs.token }}

View File

@@ -39,7 +39,7 @@ jobs:
cache-dependency-path: '**/pnpm-lock.yaml'
- name: Fix formatting
run: make install-all && make format-all
run: pnpm --recursive install && pnpm run --recursive --parallel fix:format
- name: Commit and push
uses: EndBug/add-and-commit@a94899bca583c204427a224a7af87c02f9b325d5 # v9.1.4

View File

@@ -99,8 +99,23 @@ jobs:
ALIAS: ${{ secrets.ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
# iOS secrets
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ISSUER_ID }}
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
IOS_CERTIFICATE_P12: ${{ secrets.IOS_CERTIFICATE_P12 }}
IOS_CERTIFICATE_PASSWORD: ${{ secrets.IOS_CERTIFICATE_PASSWORD }}
IOS_PROVISIONING_PROFILE: ${{ secrets.IOS_PROVISIONING_PROFILE }}
IOS_PROVISIONING_PROFILE_SHARE_EXTENSION: ${{ secrets.IOS_PROVISIONING_PROFILE_SHARE_EXTENSION }}
IOS_PROVISIONING_PROFILE_WIDGET_EXTENSION: ${{ secrets.IOS_PROVISIONING_PROFILE_WIDGET_EXTENSION }}
IOS_DEVELOPMENT_PROVISIONING_PROFILE: ${{ secrets.IOS_DEVELOPMENT_PROVISIONING_PROFILE }}
IOS_DEVELOPMENT_PROVISIONING_PROFILE_SHARE_EXTENSION: ${{ secrets.IOS_DEVELOPMENT_PROVISIONING_PROFILE_SHARE_EXTENSION }}
IOS_DEVELOPMENT_PROVISIONING_PROFILE_WIDGET_EXTENSION: ${{ secrets.IOS_DEVELOPMENT_PROVISIONING_PROFILE_WIDGET_EXTENSION }}
FASTLANE_TEAM_ID: ${{ secrets.FASTLANE_TEAM_ID }}
with:
ref: ${{ needs.bump_version.outputs.ref }}
environment: production
prepare_release:
runs-on: ubuntu-latest

View File

@@ -1,6 +1,6 @@
{
"name": "@immich/cli",
"version": "2.2.98",
"version": "2.2.100",
"description": "Command Line Interface (CLI) for Immich",
"type": "module",
"exports": "./dist/index.js",

View File

@@ -10,6 +10,16 @@ import MobileAppBackup from '/docs/partials/_mobile-app-backup.md';
<MobileAppDownload />
:::info Android verification
Below are the SHA-256 fingerprints for the certificates signing the android applications.
- Playstore / Github releases:
`86:C5:C4:55:DF:AF:49:85:92:3A:8F:35:AD:B3:1D:0C:9E:0B:95:7D:7F:94:C2:D2:AF:6A:24:38:AA:96:00:20`
- F-Droid releases:
`FA:8B:43:95:F4:A6:47:71:A0:53:D1:C7:57:73:5F:A2:30:13:74:F5:3D:58:0D:D1:75:AA:F7:A1:35:72:9C:BF`
:::
:::info Beta Program
The beta release channel allows users to test upcoming changes before they are officially released. To join the channel use the links below.

View File

@@ -1,4 +1,12 @@
[
{
"label": "v2.2.2",
"url": "https://docs.v2.2.2.archive.immich.app"
},
{
"label": "v2.2.1",
"url": "https://docs.v2.2.1.archive.immich.app"
},
{
"label": "v2.2.0",
"url": "https://docs.v2.2.0.archive.immich.app"

View File

@@ -1,6 +1,6 @@
{
"name": "immich-e2e",
"version": "2.2.0",
"version": "2.2.2",
"description": "",
"main": "index.js",
"type": "module",

View File

@@ -1716,6 +1716,7 @@
"running": "Kører",
"save": "Gem",
"save_to_gallery": "Gem til galleri",
"saved": "Gemt",
"saved_api_key": "Gemt API-nøgle",
"saved_profile": "Gemte profil",
"saved_settings": "Gemte indstillinger",

View File

@@ -155,12 +155,15 @@
"machine_learning_min_recognized_faces": "Mindestens erkannte Gesichter",
"machine_learning_min_recognized_faces_description": "Die Mindestanzahl von erkannten Gesichtern, damit eine Person erstellt werden kann. Eine Erhöhung dieses Wertes macht die Gesichtserkennung präziser, erhöht aber die Wahrscheinlichkeit, dass ein Gesicht nicht zu einer Person zugeordnet wird.",
"machine_learning_ocr": "OCR",
"machine_learning_ocr_description": "Maschinen lernen nutzen um Texte in Bildern zu erkennen",
"machine_learning_ocr_description": "Maschinelles Lernen nutzen um Texte in Bildern zu erkennen",
"machine_learning_ocr_enabled": "OCR aktivieren",
"machine_learning_ocr_enabled_description": "Wenn deaktiviert, werden die Bilder nicht von der Texterkennung bearbeitet.",
"machine_learning_ocr_max_resolution": "Maximale Auflösung",
"machine_learning_ocr_max_resolution_description": "Vorschauen über dieser Auflösung werden unter Beibehaltung des Seitenverhältnisses verkleinert. Höhere Werte sind genauer, benötigen jedoch mehr Zeit für die Verarbeitung und verbrauchen mehr Speicher.",
"machine_learning_ocr_min_detection_score": "Minimaler Erkennungswert",
"machine_learning_ocr_min_detection_score_description": "Minimale Konfidenzrate für die Texterkennung von 01. Niedrigere Werte führen dazu, dass mehr Text erkannt wird, können jedoch zu falsch-positiven Ergebnissen führen.",
"machine_learning_ocr_min_recognition_score": "Minimale Erkennungsrate",
"machine_learning_ocr_min_score_recognition_description": "Minimale Konfidenzrate für die Erkennung von erkanntem Text von 01. Niedrigere Werte führen dazu, dass mehr Text erkannt wird, können jedoch zu falsch-positiven Ergebnissen führen.",
"machine_learning_ocr_model": "OCR Modell",
"machine_learning_ocr_model_description": "Server Modelle sind genauer als mobile Modelle, brauchen aber länger zur Verarbeitung und brauchen mehr Speicher.",
"machine_learning_settings": "Einstellungen für maschinelles Lernen",
@@ -254,7 +257,7 @@
"oauth_storage_quota_default_description": "Kontingent in GiB, das verwendet werden soll, wenn keines übermittelt wird.",
"oauth_timeout": "Zeitüberschreitung bei Anfrage",
"oauth_timeout_description": "Zeitüberschreitung für Anfragen in Millisekunden",
"ocr_job_description": "Verwende Machine Learning zur Ernennung von Text in Bildern",
"ocr_job_description": "Verwende Machine Learning zur Erkennung von Text in Bildern",
"password_enable_description": "Mit E-Mail und Passwort anmelden",
"password_settings": "Passwort-Anmeldung",
"password_settings_description": "Passwort-Anmeldeeinstellungen verwalten",
@@ -1352,7 +1355,7 @@
"memories_check_back_tomorrow": "Schau morgen wieder vorbei für weitere Erinnerungen",
"memories_setting_description": "Verwalte, was du in deinen Erinnerungen siehst",
"memories_start_over": "Erneut beginnen",
"memories_swipe_to_close": "Nach oben Wischen zum schließen",
"memories_swipe_to_close": "Nach oben Wischen zum Schließen",
"memory": "Erinnerung",
"memory_lane_title": "Foto-Erinnerungen {title}",
"menu": "Menü",
@@ -1713,6 +1716,7 @@
"running": "Läuft",
"save": "Speichern",
"save_to_gallery": "In Galerie speichern",
"saved": "Gespeichert",
"saved_api_key": "API-Schlüssel wurde gespeichert",
"saved_profile": "Profil gespeichert",
"saved_settings": "Einstellungen gespeichert",

View File

@@ -157,7 +157,7 @@
"machine_learning_ocr": "OCR",
"machine_learning_ocr_description": "Utiliser l'apprentissage automatique pour reconnaître le texte dans les images",
"machine_learning_ocr_enabled": "Activer la reconnaissance de caractères",
"machine_learning_ocr_enabled_description": "Si désactivé, la reconnaissance de texte ne s'appliquera pas aux images",
"machine_learning_ocr_enabled_description": "Si désactivé, la reconnaissance de texte ne s'appliquera pas aux images.",
"machine_learning_ocr_max_resolution": "Résolution maximale",
"machine_learning_ocr_max_resolution_description": "Les prévisualisations au-dessus de cette résolution seront retaillées en conservant leur ratio. Des valeurs plus grandes sont plus précises, mais sont plus lentes et utilisent plus de mémoire.",
"machine_learning_ocr_min_detection_score": "Score minimum de détection",

View File

@@ -33,6 +33,7 @@
"add_to_albums": "एकाधिक एल्बम में डाले",
"add_to_albums_count": "एल्बमों में डालें ({count})",
"add_to_shared_album": "शेयर किए गए एल्बम में डालें",
"add_upload_to_stack": "स्टैक में अपलोड करें",
"add_url": "URL डालें",
"added_to_archive": "संग्रहीत कर दिया गया है",
"added_to_favorites": "पसंदीदा में डाला गया",
@@ -124,6 +125,13 @@
"logging_enable_description": "लॉगिंग करने देना",
"logging_level_description": "सक्षम होने पर, किस लॉग स्तर का उपयोग करना है।",
"logging_settings": "लॉगिंग",
"machine_learning_availability_checks": "उपलब्धता जांच",
"machine_learning_availability_checks_description": "उपलब्ध मशीन लर्निंग सर्वर का स्वचालित रूप से पता लगाएं और प्राथमिकता दें",
"machine_learning_availability_checks_enabled": "उपलब्धता जांच सक्षम करें",
"machine_learning_availability_checks_interval": "अंतराल की जाँच करें",
"machine_learning_availability_checks_interval_description": "उपलब्धता जांच के बीच मिलीसेकेंड में अंतराल",
"machine_learning_availability_checks_timeout": "अनुरोध समयबाह्य हुआ",
"machine_learning_availability_checks_timeout_description": "उपलब्धता जांच के लिए मिलीसेकंड में समयबाह्य अंतराल",
"machine_learning_clip_model": "क्लिप मॉडल",
"machine_learning_clip_model_description": "CLIP मॉडल का नाम <link>यहां</link> सूचीबद्ध है। ध्यान दें कि मॉडल बदलने पर आपको सभी छवियों के लिए 'स्मार्ट सर्च' जोब फिर से चलाना होगा।",
"machine_learning_duplicate_detection": "डुप्लिकेट का पता लगाना",
@@ -146,6 +154,18 @@
"machine_learning_min_detection_score_description": "किसी चेहरे का पता लगाने के लिए न्यूनतम आत्मविश्वास स्कोर 0-1 होना चाहिए।",
"machine_learning_min_recognized_faces": "निम्नतम पहचाने चेहरे",
"machine_learning_min_recognized_faces_description": "किसी व्यक्ति के लिए पहचाने जाने वाले चेहरों की न्यूनतम संख्या।",
"machine_learning_ocr": "ओ.सी.आर",
"machine_learning_ocr_description": "चित्रों में पाठ को पहचानने के लिए मशीन लर्निंग का उपयोग करें",
"machine_learning_ocr_enabled": "ओ.सी.आर. सक्षम करें",
"machine_learning_ocr_enabled_description": "यदि अक्षम किया गया है, तो चित्रों पर पाठ-पहचान नहीं होगा।",
"machine_learning_ocr_max_resolution": "अधिकतम रिज़ॉल्यूशन",
"machine_learning_ocr_max_resolution_description": "इस रिज़ॉल्यूशन से ऊपर के प्रदर्शन का आकार मूल अनुपात को संरक्षित करते हुए बदल दिया जाएगा। उच्च मान अधिक सटीक होते हैं, लेकिन संसाधित होने में अधिक मेमोरी और समय लगाते हैं।",
"machine_learning_ocr_min_detection_score": "न्यूनतम खोज अंक",
"machine_learning_ocr_min_detection_score_description": "पाठ का पता लगाने के लिए 0-1 के बीच न्यूनतम आत्मविश्वास अंक। कम अंक अधिक पाठ का पता लगाएंगे लेकिन परिणाम गलत हो सकते हैं।",
"machine_learning_ocr_min_recognition_score": "न्यूनतम पहचान अंक",
"machine_learning_ocr_min_score_recognition_description": "पाठ को पहचानने के लिए 0-1 के बीच न्यूनतम आत्मविश्वास अंक। कम अंक अधिक पाठ को पहचानेंगे लेकिन परिणाम गलत हो सकते हैं।",
"machine_learning_ocr_model": "ओसीआर प्रतिमान",
"machine_learning_ocr_model_description": "सर्वर प्रतिमान मोबाइल प्रतिमान की तुलना में अधिक सटीक होते हैं, लेकिन संसाधित होने में अधिक मेमोरी और समय लेते हैं।",
"machine_learning_settings": "मशीन लर्निंग सेटिंग्स",
"machine_learning_settings_description": "मशीन लर्निंग सुविधाओं और सेटिंग्स को प्रबंधित करें",
"machine_learning_smart_search": "स्मार्ट खोज",
@@ -203,6 +223,8 @@
"notification_email_ignore_certificate_errors_description": "टीएलएस प्रमाणपत्र सत्यापन त्रुटियों पर ध्यान न दें (अनुशंसित नहीं)",
"notification_email_password_description": "ईमेल सर्वर से प्रमाणीकरण करते समय उपयोग किया जाने वाला पासवर्ड",
"notification_email_port_description": "ईमेल सर्वर का पोर्ट (जैसे 25, 465, या 587)",
"notification_email_secure": "एस एम टी पी एस",
"notification_email_secure_description": "एस.एम.टी.पी.एस. प्रयोग करें (टी.एल.एस पर एस.एम.टी.पी)",
"notification_email_sent_test_email_button": "परीक्षण ईमेल भेजें और सहेजें",
"notification_email_setting_description": "ईमेल सूचनाएं भेजने के लिए सेटिंग्स",
"notification_email_test_email": "परीक्षण ईमेल भेजें",
@@ -235,6 +257,7 @@
"oauth_storage_quota_default_description": "GiB में कोटा का उपयोग तब किया जाएगा जब कोई दावा प्रदान नहीं किया गया हो ।",
"oauth_timeout": "ब्रेक का अनुरोध",
"oauth_timeout_description": "अनुरोधों के लिए समय-सीमा मिलीसेकंड में",
"ocr_job_description": "चित्रों में पाठ को पहचानने के लिए मशीन लर्निंग का उपयोग करें",
"password_enable_description": "ईमेल और पासवर्ड से लॉगिन करें",
"password_settings": "पासवर्ड लॉग इन",
"password_settings_description": "पासवर्ड लॉगिन सेटिंग प्रबंधित करें",
@@ -325,7 +348,7 @@
"transcoding_max_b_frames": "अधिकतम बी-फ्रेम",
"transcoding_max_b_frames_description": "उच्च मान संपीड़न दक्षता में सुधार करते हैं, लेकिन एन्कोडिंग को धीमा कर देते हैं।",
"transcoding_max_bitrate": "अधिकतम बिटरेट",
"transcoding_max_bitrate_description": "अधिकतम बिटरेट सेट करने से फ़ाइल आकार को गुणवत्ता पर मामूली लागत के साथ अधिक पूर्वानुमानित किया जा सकता है। 720p पर, सामान्य मान VP9 या HEVC के लिए 2600k kbit/s या H.264 के लिए 4500k kbit/s हैं। 0 पर सेट होने पर अक्षम।",
"transcoding_max_bitrate_description": "अधिकतम बिटरेट सेट करने से फ़ाइल आकार को गुणवत्ता पर मामूली लागत के साथ अधिक पूर्वानुमानित किया जा सकता है। 720p पर, सामान्य मान VP9 या HEVC के लिए 2600k kbit/s या H.264 के लिए 4500k kbit/s हैं। 0 पर सेट होने पर अक्षम। जब कोई इकाई निर्दिष्ट नहीं की जाती है, तो k (kbit/s के लिए) मान लिया जाता है; इसलिए 5000, 5000k, और 5M (Mbit/s के लिए) समतुल्य हैं।",
"transcoding_max_keyframe_interval": "अधिकतम मुख्यफ़्रेम अंतराल",
"transcoding_max_keyframe_interval_description": "मुख्यफ़्रेम के बीच अधिकतम फ़्रेम दूरी निर्धारित करता है।",
"transcoding_optimal_description": "लक्ष्य रिज़ॉल्यूशन से अधिक ऊंचे वीडियो या स्वीकृत प्रारूप में नहीं",
@@ -359,6 +382,9 @@
"trash_number_of_days_description": "संपत्तियों को स्थायी रूप से हटाने से पहले उन्हें कूड़ेदान में रखने के लिए दिनों की संख्या",
"trash_settings": "ट्रैश सेटिंग",
"trash_settings_description": "ट्रैश सेटिंग प्रबंधित करें",
"unlink_all_oauth_accounts": "सभी ओ.औथ खातों से संपर्क तोड़ दें",
"unlink_all_oauth_accounts_description": "नए प्रदाता पर सतानांतरण करने से पहले सभी ओ.औथ खातों से संपर्क तोड़ना याद रखें।",
"unlink_all_oauth_accounts_prompt": "क्या आप वाकई सभी ओ.औथ खातों से संपर्क तोड़ना चाहते हैं? इससे प्रत्येक उपयोगकर्ता के लिए ओ.औथ आई.डी रद्द हो जाएगी और इसे पूर्ववत नहीं किया जा सकेगा।",
"user_cleanup_job": "उपयोगकर्ता सफ़ाई",
"user_delete_delay": "<b>{user}</b> के खाते और परिसंपत्तियों को {delay, plural, one {# day} other {# days}} में स्थायी रूप से हटाने के लिए शेड्यूल किया जाएगा।",
"user_delete_delay_settings": "हटाने में देरी",
@@ -392,6 +418,8 @@
"advanced_settings_prefer_remote_title": "दूरस्थ छवियों को प्राथमिकता दें",
"advanced_settings_proxy_headers_subtitle": "प्रत्येक नेटवर्क अनुरोध के साथ इम्मिच द्वारा भेजे जाने वाले प्रॉक्सी हेडर को परिभाषित करें",
"advanced_settings_proxy_headers_title": "प्रॉक्सी हेडर",
"advanced_settings_readonly_mode_subtitle": "रीड-ओनली प्रणाली को सक्षम करता है जहां चित्र को केवल देखा जा सकता है, एकाधिक चित्रों का चयन करना, साझा करना, कास्टिंग करना, हटाना जैसी सभी चीज़ें अक्षम हैं। मुख्य स्क्रीन में उपयोगकर्ता- अवतार के माध्यम से रीड-ओनली प्रणाली को सक्षम/अक्षम करें",
"advanced_settings_readonly_mode_title": "रीड-ओनली प्रणाली",
"advanced_settings_self_signed_ssl_subtitle": "सर्वर एंडपॉइंट के लिए SSL प्रमाणपत्र सत्यापन को छोड़ देता है। स्व-हस्ताक्षरित प्रमाणपत्रों के लिए आवश्यक है।",
"advanced_settings_self_signed_ssl_title": "स्व-हस्ताक्षरित SSL प्रमाणपत्रों की अनुमति दें",
"advanced_settings_sync_remote_deletions_subtitle": "वेब पर कार्रवाई किए जाने पर इस डिवाइस पर किसी संपत्ति को स्वचालित रूप से हटाएँ या पुनर्स्थापित करें",
@@ -419,6 +447,7 @@
"album_remove_user_confirmation": "क्या आप वाकई {user} को हटाना चाहते हैं?",
"album_search_not_found": "आपकी खोज से मेल खाता कोई एल्बम नहीं मिला",
"album_share_no_users": "ऐसा लगता है कि आपने यह एल्बम सभी उपयोगकर्ताओं के साथ साझा कर दिया है या आपके पास साझा करने के लिए कोई उपयोगकर्ता नहीं है।",
"album_summary": "एल्बम सारांश",
"album_updated": "एल्बम अपडेट किया गया",
"album_updated_setting_description": "जब किसी साझा एल्बम में नई संपत्तियाँ हों तो एक ईमेल सूचना प्राप्त करें",
"album_user_left": "बायाँ {album}",
@@ -452,11 +481,16 @@
"api_key_description": "यह की केवल एक बार दिखाई जाएगी। विंडो बंद करने से पहले कृपया इसे कॉपी करना सुनिश्चित करें।।",
"api_key_empty": "आपका एपीआई कुंजी नाम खाली नहीं होना चाहिए",
"api_keys": "एपीआई कीज",
"app_architecture_variant": "रूपान्तर (स्थापत्य/आर्किटेक्चर)",
"app_bar_signout_dialog_content": "क्या आप सुनिश्चित हैं कि आप लॉग आउट करना चाहते हैं?",
"app_bar_signout_dialog_ok": "हाँ",
"app_bar_signout_dialog_title": "लॉग आउट",
"app_download_links": "ऐप डाउनलोड लिंक",
"app_settings": "एप्लिकेशन सेटिंग",
"app_stores": "ऐप स्टोर/गोदाम",
"app_update_available": "आधुनिक ऐप उपलब्ध है",
"appears_in": "प्रकट होता है",
"apply_count": "लागू करें ({count, number})",
"archive": "संग्रहालय",
"archive_action_prompt": "{count} को संग्रह में जोड़ा गया",
"archive_or_unarchive_photo": "फ़ोटो को संग्रहीत या असंग्रहीत करें",
@@ -465,17 +499,17 @@
"archive_size": "पुरालेख आकार",
"archive_size_description": "डाउनलोड के लिए संग्रह आकार कॉन्फ़िगर करें (GiB में)",
"archived": "संग्रहित",
"archived_count": "{count, plural, other {# संग्रहीत किए गए}",
"archived_count": "{count, plural, other {# संग्रहीत किए गए}}",
"are_these_the_same_person": "क्या ये वही व्यक्ति हैं?",
"are_you_sure_to_do_this": "क्या आप वास्तव में इसे करना चाहते हैं?",
"asset_action_delete_err_read_only": "केवल पढ़ने योग्य परिसंपत्ति(ओं) को हटाया नहीं जा सकता, छोड़ा जा सकता है",
"asset_action_share_err_offline": "ऑफ़लाइन परिसंपत्ति(एँ) प्राप्त नहीं की जा सकती, छोड़ी जा रही है",
"asset_added_to_album": "एल्बम में डाला गया",
"asset_adding_to_album": "एल्बम में डाला जा रहा है..।",
"asset_adding_to_album": "एल्बम में डाला जा रहा है",
"asset_description_updated": "संपत्ति विवरण अद्यतन कर दिया गया है",
"asset_filename_is_offline": "एसेट {filename} ऑफ़लाइन है",
"asset_has_unassigned_faces": "एसेट में अनिर्धारित चेहरे हैं",
"asset_hashing": "हैशिंग...।",
"asset_hashing": "हैशिंग",
"asset_list_group_by_sub_title": "द्वारा समूह बनाएं",
"asset_list_layout_settings_dynamic_layout_title": "गतिशील लेआउट",
"asset_list_layout_settings_group_automatically": "स्वचालित",
@@ -489,6 +523,8 @@
"asset_restored_successfully": "संपत्ति(याँ) सफलतापूर्वक पुनर्स्थापित की गईं",
"asset_skipped": "छोड़ा गया",
"asset_skipped_in_trash": "कचरे में",
"asset_trashed": "एसेट नष्ट किया गया",
"asset_troubleshoot": "एसेट समस्या निवारण",
"asset_uploaded": "अपलोड किए गए",
"asset_uploading": "अपलोड हो रहा है…",
"asset_viewer_settings_subtitle": "अपनी गैलरी व्यूअर सेटिंग प्रबंधित करें",
@@ -496,7 +532,9 @@
"assets": "संपत्तियां",
"assets_added_count": "{count, plural, one {# asset} other {# assets}} जोड़ा गया",
"assets_added_to_album_count": "एल्बम में {count, plural, one {# asset} other {# assets}} जोड़ा गया",
"assets_added_to_albums_count": "{assetTotal, plural, one {# asset} other {# assets}} को {albumTotal, plural, one {# album} other {# albums}} से जोड़ा गया",
"assets_cannot_be_added_to_album_count": "{count, plural, one {Asset} other {Assets}} को एल्बम में नहीं जोड़ा जा सकता",
"assets_cannot_be_added_to_albums": "{count, plural, one {Asset} other {Assets}} किसी एल्बम से नहीं जोड़े जा सकते",
"assets_count": "{count, plural, one {# आइटम} other {# आइटम्स}}",
"assets_deleted_permanently": "{count} संपत्ति(याँ) स्थायी रूप से हटा दी गईं",
"assets_deleted_permanently_from_server": "{count} संपत्ति(याँ) इमिच सर्वर से स्थायी रूप से हटा दी गईं",
@@ -513,14 +551,17 @@
"assets_trashed_count": "ट्रैश की गई {count, plural, one {# asset} other {# assets}}",
"assets_trashed_from_server": "{count} संपत्ति(याँ) इमिच सर्वर से कचरे में डाली गईं",
"assets_were_part_of_album_count": "{count, plural, one {Asset was} other {Assets were}}एल्बम का पहले से ही हिस्सा थे",
"assets_were_part_of_albums_count": "{count, plural, one {Asset was} other {Assets were}} पहले ही एल्बम में संयोजित हैं",
"authorized_devices": "अधिकृत उपकरण",
"automatic_endpoint_switching_subtitle": "उपलब्ध होने पर निर्दिष्ट वाई-फाई से स्थानीय रूप से कनेक्ट करें और अन्यत्र वैकल्पिक कनेक्शन का उपयोग करें",
"automatic_endpoint_switching_title": "स्वचालित URL स्विचिंग",
"autoplay_slideshow": "ऑटोप्ले स्लाइड शो",
"back": "वापस",
"back_close_deselect": "वापस जाएँ, बंद करें, या अचयनित करें",
"background_backup_running_error": "परिप्रेक्ष्य बैकअप अभी जारी है, नियमावली बैकअप प्रारंभ नहीं किया जा सकता",
"background_location_permission": "पृष्ठभूमि स्थान अनुमति",
"background_location_permission_content": "पृष्ठभूमि में चलते समय नेटवर्क बदलने के लिए, Immich के पास *हमेशा* सटीक स्थान तक पहुंच होनी चाहिए ताकि ऐप वाई-फाई नेटवर्क का नाम पढ़ सके",
"background_options": "परिप्रेक्ष्य विकल्प",
"backup": "बैकअप",
"backup_album_selection_page_albums_device": "डिवाइस पर एल्बम ({count})",
"backup_album_selection_page_albums_tap": "शामिल करने के लिए टैप करें, बाहर करने के लिए डबल टैप करें",
@@ -528,8 +569,10 @@
"backup_album_selection_page_select_albums": "एल्बम चुनें",
"backup_album_selection_page_selection_info": "चयन जानकारी",
"backup_album_selection_page_total_assets": "कुल अद्वितीय संपत्तियाँ",
"backup_albums_sync": "बैकअप एल्बम का तुल्यकालन",
"backup_all": "सभी",
"backup_background_service_backup_failed_message": "संपत्तियों का बैकअप लेने में विफल. पुनः प्रयास किया जा रहा है…",
"backup_background_service_complete_notification": "एसेट का बैकअप पूरा हुआ",
"backup_background_service_connection_failed_message": "सर्वर से कनेक्ट करने में विफल. पुनः प्रयास किया जा रहा है…",
"backup_background_service_current_upload_notification": "{filename} अपलोड हो रहा है",
"backup_background_service_default_notification": "नई परिसंपत्तियों की जांच की जा रही है…",
@@ -577,6 +620,7 @@
"backup_controller_page_turn_on": "अग्रभूमि बैकअप चालू करें",
"backup_controller_page_uploading_file_info": "फ़ाइल जानकारी अपलोड करना",
"backup_err_only_album": "एकमात्र एल्बम नहीं हटाया जा सकता",
"backup_error_sync_failed": "तुल्यकालन विफल. बैकअप संसाधित नहीं किया जा सकता।",
"backup_info_card_assets": "संपत्ति",
"backup_manual_cancelled": "रद्द",
"backup_manual_in_progress": "अपलोड पहले से ही प्रगति पर है। कुछ देर बाद प्रयास करें",
@@ -638,12 +682,16 @@
"change_password_description": "यह या तो पहली बार है जब आप सिस्टम में साइन इन कर रहे हैं या आपका पासवर्ड बदलने का अनुरोध किया गया है।",
"change_password_form_confirm_password": "पासवर्ड की पुष्टि कीजिये",
"change_password_form_description": "नमस्ते {name},\n\nया तो आप पहली बार सिस्टम में साइन इन कर रहे हैं या फिर आपका पासवर्ड बदलने का अनुरोध किया गया है। कृपया नीचे नया पासवर्ड डालें।",
"change_password_form_log_out": "अन्य सभी डिवाइस को लॉग आउट करें",
"change_password_form_log_out_description": "अन्य सभी डिवाइस से लॉग आउट करना अनुशंसित है",
"change_password_form_new_password": "नया पासवर्ड",
"change_password_form_password_mismatch": "सांकेतिक शब्द मेल नहीं खाते",
"change_password_form_reenter_new_password": "नया पासवर्ड पुनः दर्ज करें",
"change_pin_code": "पिन कोड बदलें",
"change_your_password": "अपना पासवर्ड बदलें",
"changed_visibility_successfully": "दृश्यता सफलतापूर्वक परिवर्तित",
"charging": "चार्जिंग",
"charging_requirement_mobile_backup": "परिप्रेक्ष्य बैकअप के लिए डिवाइस का चार्जिंग पे लगे होना आवश्यक है",
"check_corrupt_asset_backup": "दूषित परिसंपत्ति बैकअप की जाँच करें",
"check_corrupt_asset_backup_button": "जाँच करें",
"check_corrupt_asset_backup_description": "यह जाँच केवल वाई-फ़ाई पर ही करें और सभी संपत्तियों का बैकअप लेने के बाद ही करें। इस प्रक्रिया में कुछ मिनट लग सकते हैं।",
@@ -665,7 +713,7 @@
"client_cert_subtitle": "केवल PKCS12 (.p12, .pfx) फ़ॉर्मैट का समर्थन करता है। प्रमाणपत्र आयात/हटाएँ केवल लॉगिन से पहले उपलब्ध हैं",
"client_cert_title": "SSL क्लाइंट प्रमाणपत्र",
"clockwise": "दक्षिणावर्त",
"close": "बंद",
"close": "बंद करें",
"collapse": "गिर जाना",
"collapse_all": "सभी को संकुचित करें",
"color": "रंग",
@@ -675,8 +723,8 @@
"comments_and_likes": "टिप्पणियाँ और पसंद",
"comments_are_disabled": "टिप्पणियाँ अक्षम हैं",
"common_create_new_album": "नया एल्बम बनाएँ",
"completed": "पुरा होना",
"confirm": "पुष्टि",
"completed": "पूरित",
"confirm": "पुष्टि करें",
"confirm_admin_password": "एडमिन पासवर्ड की पुष्टि करें",
"confirm_delete_face": "क्या आप वाकई एसेट से {name} चेहरा हटाना चाहते हैं?",
"confirm_delete_shared_link": "क्या आप वाकई इस साझा लिंक को हटाना चाहते हैं?",
@@ -685,13 +733,13 @@
"confirm_password": "पासवर्ड की पुष्टि कीजिये",
"confirm_tag_face": "क्या आप इस चेहरे को {name} के रूप में टैग करना चाहते हैं?",
"confirm_tag_face_unnamed": "क्या आप इस चेहरे को टैग करना चाहते हैं?",
"connected_device": "कनेक्टेड डिवाइस",
"connected_device": "योजित यंत्र",
"connected_to": "से जुड़ा",
"contain": "समाहित",
"context": "संदर्भ",
"continue": "जारी",
"control_bottom_app_bar_create_new_album": "नया एल्बम बनाएँ",
"control_bottom_app_bar_delete_from_immich": "Immich से हटाएं",
"control_bottom_app_bar_delete_from_immich": "इम्मिच से हटाएं",
"control_bottom_app_bar_delete_from_local": "डिवाइस से हटाएं",
"control_bottom_app_bar_edit_location": "स्थान संपादित करें",
"control_bottom_app_bar_edit_time": "तारीख और समय संपादित करें",
@@ -713,6 +761,7 @@
"create": "तैयार करें",
"create_album": "एल्बम बनाओ",
"create_album_page_untitled": "शीर्षकहीन",
"create_api_key": "ऐ.पी.आई. चाभी बनाएं",
"create_library": "लाइब्रेरी बनाएं",
"create_link": "लिंक बनाएं",
"create_link_to_share": "शेयर करने के लिए लिंक बनाएं",
@@ -729,6 +778,7 @@
"create_user": "उपयोगकर्ता बनाइये",
"created": "बनाया",
"created_at": "बनाया था",
"creating_linked_albums": "जुड़े हुए एल्बम बनाए जा रहे हैं..।",
"crop": "छाँटें",
"curated_object_page_title": "चीज़ें",
"current_device": "वर्तमान उपकरण",
@@ -741,6 +791,7 @@
"daily_title_text_date_year": "ई, एमएमएम दिन, वर्ष",
"dark": "डार्क",
"dark_theme": "डार्क थीम टॉगल करें",
"date": "दिनांक",
"date_after": "इसके बाद की तारीख",
"date_and_time": "तिथि और समय",
"date_before": "पहले की तारीख",
@@ -748,6 +799,7 @@
"date_of_birth_saved": "जन्मतिथि सफलतापूर्वक सहेजी गई",
"date_range": "तिथि सीमा",
"day": "दिन",
"days": "दिन",
"deduplicate_all": "सभी को डुप्लिकेट करें",
"deduplication_criteria_1": "छवि का आकार बाइट्स में",
"deduplication_criteria_2": "EXIF डेटा की संख्या",
@@ -836,6 +888,8 @@
"edit_date": "संपादन की तारीख",
"edit_date_and_time": "दिनांक और समय संपादित करें",
"edit_date_and_time_action_prompt": "{count} तारीख और समय संपादित किए गए",
"edit_date_and_time_by_offset": "अंकुर से दिनांक बदलें",
"edit_date_and_time_by_offset_interval": "नयी दिनांक सीमा: {from} - {to}",
"edit_description": "संपादित करें वर्णन",
"edit_description_prompt": "कृपया एक नया विवरण चुनें:",
"edit_exclusion_pattern": "बहिष्करण पैटर्न संपादित करें",
@@ -874,7 +928,9 @@
"error": "गलती",
"error_change_sort_album": "एल्बम का क्रम बदलने में असफल रहा",
"error_delete_face": "एसेट से चेहरे को हटाने में त्रुटि हुई",
"error_getting_places": "स्थानों को प्राप्त करने में त्रुटि हुई",
"error_loading_image": "छवि लोड करने में त्रुटि",
"error_loading_partners": "जोड़ीदार लोड करने में त्रुटि हुई: {error}",
"error_saving_image": "त्रुटि: {error}",
"error_tag_face_bounding_box": "चेहरे को टैग करने में त्रुटि बाउंडिंग बॉक्स निर्देशांक प्राप्त नहीं कर सके",
"error_title": "त्रुटि - कुछ गलत हो गया",
@@ -907,6 +963,7 @@
"failed_to_load_notifications": "सूचनाएँ लोड करने में विफल",
"failed_to_load_people": "लोगों को लोड करने में विफल",
"failed_to_remove_product_key": "उत्पाद कुंजी निकालने में विफल",
"failed_to_reset_pin_code": "पिन कोड रीसेट करना विफल हुआ",
"failed_to_stack_assets": "परिसंपत्तियों का ढेर लगाने में विफल",
"failed_to_unstack_assets": "परिसंपत्तियों का ढेर खोलने में विफल",
"failed_to_update_notification_status": "सूचना की स्थिति अपडेट करने में विफल",
@@ -915,6 +972,7 @@
"paths_validation_failed": "{paths, plural, one {# पथ} other {# पथ}} सत्यापन में विफल रहे",
"profile_picture_transparent_pixels": "प्रोफ़ाइल चित्रों में पारदर्शी पिक्सेल नहीं हो सकते।",
"quota_higher_than_disk_size": "आपने डिस्क आकार से अधिक कोटा निर्धारित किया है",
"something_went_wrong": "कुछ त्रुटि हुई",
"unable_to_add_album_users": "उपयोगकर्ताओं को एल्बम में डालने में असमर्थ",
"unable_to_add_assets_to_shared_link": "साझा लिंक में संपत्ति डालने में असमर्थ",
"unable_to_add_comment": "टिप्पणी डालने में असमर्थ",
@@ -1000,22 +1058,42 @@
},
"exif": "एक्सिफ",
"exif_bottom_sheet_description": "विवरण जोड़ें..।",
"exif_bottom_sheet_description_error": "विवरण के आधुनीकरण करने में त्रुटि हुई",
"exif_bottom_sheet_details": "विवरण",
"exif_bottom_sheet_location": "स्थान",
"exif_bottom_sheet_no_description": "कोई विवरण नहीं",
"exif_bottom_sheet_people": "लोग",
"exif_bottom_sheet_person_add_person": "नाम डालें",
"exit_slideshow": "स्लाइड शो से बाहर निकलें",
"expand_all": "सभी का विस्तार",
"experimental_settings_new_asset_list_subtitle": "कार्य प्रगति पर है",
"experimental_settings_new_asset_list_title": "प्रयोगात्मक फोटो ग्रिड सक्षम करें",
"experimental_settings_subtitle": "अपने जोखिम पर उपयोग करें!",
"experimental_settings_title": "प्रयोगात्मक",
"expire_after": "एक्सपायर आफ्टर",
"expired": "खत्म हो चुका",
"expires_date": "{date} को समाप्त हो रहा है",
"explore": "अन्वेषण करना",
"explorer": "समन्वेषक",
"export": "निर्यात",
"export_as_json": "JSON के रूप में निर्यात करें",
"export_database": "डेटाबेस निर्यात करें",
"export_database_description": "इस.क्यू.लाइट डेटाबेस निर्यात करें",
"extension": "विस्तार",
"external": "बाहरी",
"external_libraries": "बाहरी पुस्तकालय",
"external_network": "बाहरी नेटवर्क",
"external_network_sheet_info": "When not on the preferred WiFi network, the app will connect to the server through the first of the below URLs it can reach, starting from top to bottom",
"face_unassigned": "सौंपे नहीं गए",
"failed": "विफल हुआ",
"failed_to_authenticate": "प्रमाणित करने में विफल",
"failed_to_load_assets": "एसेट लोड करने में विफल",
"failed_to_load_folder": "फोल्डर लोड करने में विफल",
"favorite": "पसंदीदा",
"favorite_action_prompt": "{count} पसंदीदा संकलन में जोड़े गए",
"favorite_or_unfavorite_photo": "पसंदीदा या नापसंद फोटो",
"favorites": "पसंदीदा",
"favorites_page_no_favorites": "कोई पसंदीदा एसेट नहीं मिले",
"feature_photo_updated": "फ़ीचर फ़ोटो अपडेट किया गया",
"file_name": "फ़ाइल का नाम",
"file_name_or_extension": "फ़ाइल का नाम या एक्सटेंशन",

View File

@@ -1716,6 +1716,7 @@
"running": "Kjører",
"save": "Lagre",
"save_to_gallery": "Lagre til galleriet",
"saved": "Lagret",
"saved_api_key": "Lagret API-nøkkel",
"saved_profile": "Lagret profil",
"saved_settings": "Lagret instillinger",
@@ -1732,7 +1733,7 @@
"search_by_description_example": "Turdag i Sapa",
"search_by_filename": "Søk etter filnavn og filtype",
"search_by_filename_example": "f.eks. IMG_1234.JPG eller PNG",
"search_by_ocr": "Søk med OCR",
"search_by_ocr": "Søk etter tekst i bilde",
"search_by_ocr_example": "Latte",
"search_camera_lens_model": "Søk etter objektivmodell...",
"search_camera_make": "Søk etter kameramerke...",

View File

@@ -1716,6 +1716,7 @@
"running": "W trakcie",
"save": "Zapisz",
"save_to_gallery": "Zapisz w galerii",
"saved": "Zapisano",
"saved_api_key": "Zapisany klucz API",
"saved_profile": "Zapisany profil",
"saved_settings": "Zapisane ustawienia",

View File

@@ -344,7 +344,7 @@
"transcoding_hardware_acceleration": "Aceleração de hardware",
"transcoding_hardware_acceleration_description": "Experimental; transcodificação mais rápida, mas poderá ter qualidade inferior com a mesma taxa de bits",
"transcoding_hardware_decoding": "Decodificação de hardware",
"transcoding_hardware_decoding_setting_description": "Permite a aceleração ponta a ponta em vez de apenas acelerar a codificação. Pode não funcionar em todos os formatos de arquivo.",
"transcoding_hardware_decoding_setting_description": "Permite a aceleração ponta a ponta em vez de apenas acelerar a codificação. Pode não funcionar em todos os videos.",
"transcoding_max_b_frames": "Máximo de quadros B",
"transcoding_max_b_frames_description": "Valores mais altos melhoram a eficiência da compressão, mas tornam a codificação mais lenta. Pode não ser compatível com aceleração de hardware em dispositivos mais antigos. 0 desativa os quadros B, enquanto -1 define esse valor automaticamente.",
"transcoding_max_bitrate": "Taxa de bits máxima",
@@ -455,7 +455,7 @@
"album_viewer_appbar_delete_confirm": "Tem certeza que deseja excluir este álbum da sua conta?",
"album_viewer_appbar_share_err_delete": "Ocorreu um erro ao eliminar álbum",
"album_viewer_appbar_share_err_leave": "Ocorreu um erro ao sair do álbum",
"album_viewer_appbar_share_err_remove": "Houveram problemas ao remover arquivos do álbum",
"album_viewer_appbar_share_err_remove": "Ocorreu um erro ao remover ficheiros do álbum",
"album_viewer_appbar_share_err_title": "Ocorreu um erro ao alterar o título do álbum",
"album_viewer_appbar_share_leave": "Deixar álbum",
"album_viewer_appbar_share_to": "Compartilhar com",
@@ -494,7 +494,7 @@
"archive": "Arquivo",
"archive_action_prompt": "{count} adicionados ao Arquivo",
"archive_or_unarchive_photo": "Arquivar ou desarquivar foto",
"archive_page_no_archived_assets": "Nenhum arquivo encontrado",
"archive_page_no_archived_assets": "Nenhum ficheiro arquivado encontrado",
"archive_page_title": "Arquivo ({count})",
"archive_size": "Tamanho do arquivo",
"archive_size_description": "Configure o tamanho do arquivo para transferências (em GiB)",
@@ -502,8 +502,8 @@
"archived_count": "{count, plural, one {#Arquivado # item} other {Arquivados # itens}}",
"are_these_the_same_person": "Estas pessoas são a mesma pessoa?",
"are_you_sure_to_do_this": "Tem a certeza de que quer fazer isto?",
"asset_action_delete_err_read_only": "Não é possível excluir arquivo só leitura, ignorando",
"asset_action_share_err_offline": "Não foi possível obter os arquivos offline, ignorando",
"asset_action_delete_err_read_only": "Não é possível eliminar ficheiro só de leitura, a ignorar",
"asset_action_share_err_offline": "Não foi possível obter os ficheiros offline, a ignorar",
"asset_added_to_album": "Adicionado ao álbum",
"asset_adding_to_album": "A adicionar ao álbum…",
"asset_description_updated": "A descrição do ficheiro foi atualizada",
@@ -513,14 +513,14 @@
"asset_list_group_by_sub_title": "Agrupar por",
"asset_list_layout_settings_dynamic_layout_title": "Layout dinâmico",
"asset_list_layout_settings_group_automatically": "Automático",
"asset_list_layout_settings_group_by": "Agrupar arquivos por",
"asset_list_layout_settings_group_by": "Agrupar ficheiros por",
"asset_list_layout_settings_group_by_month_day": "Mês + dia",
"asset_list_layout_sub_title": "Disposição",
"asset_list_settings_subtitle": "Configurações de disposição da grade de fotos",
"asset_list_settings_title": "Grade de fotos",
"asset_offline": "Ficheiro Indisponível",
"asset_offline_description": "Este ficheiro externo deixou de estar disponível no disco. Contacte o seu administrador do Immich para obter ajuda.",
"asset_restored_successfully": "Arquivo restaurado com sucesso",
"asset_restored_successfully": "FIcheiro restaurado com sucesso",
"asset_skipped": "Ignorado",
"asset_skipped_in_trash": "Na reciclagem",
"asset_trashed": "Ficheiro apagado",
@@ -565,19 +565,19 @@
"backup": "Cópia de segurança",
"backup_album_selection_page_albums_device": "Álbuns no dispositivo ({count})",
"backup_album_selection_page_albums_tap": "Toque para incluir, duplo toque para excluir",
"backup_album_selection_page_assets_scatter": "Os arquivos podem estar espalhados em vários álbuns. Assim, os álbuns podem ser incluídos ou excluídos durante o processo de backup.",
"backup_album_selection_page_assets_scatter": "Os ficheros podem estar espalhados por vários álbuns. Desta forma, os álbuns podem ser incluídos ou excluídos durante o processo de cópia de segurança.",
"backup_album_selection_page_select_albums": "Selecione Álbuns",
"backup_album_selection_page_selection_info": "Informações da Seleção",
"backup_album_selection_page_total_assets": "Total de arquivos únicos",
"backup_album_selection_page_total_assets": "Total de ficheiros únicos",
"backup_albums_sync": "Cópia de segurança de sincronização de álbuns",
"backup_all": "Tudo",
"backup_background_service_backup_failed_message": "Ocorreu um erro ao efetuar cópia de segurança dos ficheiros. A tentar de novo…",
"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": "Verificando novos arquivos…",
"backup_background_service_default_notification": "A verificar se há novos ficheiros…",
"backup_background_service_error_title": "Erro de backup",
"backup_background_service_in_progress_notification": "Fazendo backup dos arquivos…",
"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}",
"backup_controller_page_albums": "Backup Álbuns",
"backup_controller_page_background_app_refresh_disabled_content": "Para utilizar a cópia de segurança em segundo plano, ative a atualização da aplicação em segundo plano em Definições > Geral > Atualização da aplicação em segundo plano.",
@@ -600,7 +600,7 @@
"backup_controller_page_backup_selected": "Selecionado: ",
"backup_controller_page_backup_sub": "Fotos e vídeos salvos em backup",
"backup_controller_page_created": "Criado em: {date}",
"backup_controller_page_desc_backup": "Ative o backup para enviar automáticamente novos arquivos para o servidor.",
"backup_controller_page_desc_backup": "Ative a cópia de segurança em primeiro plano para enviar novos ficheiros automaticamente ao abrir a aplicação.",
"backup_controller_page_excluded": "Eliminado: ",
"backup_controller_page_failed": "Falhou ({count})",
"backup_controller_page_filename": "Nome do ficheiro: {filename} [{size}]",
@@ -618,10 +618,10 @@
"backup_controller_page_total_sub": "Todas as fotos e vídeos dos álbuns selecionados",
"backup_controller_page_turn_off": "Desativar backup",
"backup_controller_page_turn_on": "Ativar backup",
"backup_controller_page_uploading_file_info": "Enviando arquivo",
"backup_controller_page_uploading_file_info": "A enviar informações do ficheiro",
"backup_err_only_album": "Não é possível remover apenas o álbum",
"backup_error_sync_failed": "A sincronização falhou. Não é possível fazer a cópia de segurança.",
"backup_info_card_assets": "arquivos",
"backup_info_card_assets": "ficheiros",
"backup_manual_cancelled": "Cancelado",
"backup_manual_in_progress": "Envio já está em progresso. Tente novamente mais tarde",
"backup_manual_success": "Sucesso",
@@ -694,7 +694,7 @@
"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_description": "Execute esta verificação somente em uma rede Wi-Fi e quando o backup de todos os arquivos já estiver concluído. O processo demora alguns minutos.",
"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",
"choose_matching_people_to_merge": "Escolha pessoas correspondentes para unir",
"city": "Cidade/Localidade",
@@ -770,7 +770,7 @@
"create_new_person": "Criar nova pessoa",
"create_new_person_hint": "Associe os ficheiros a uma nova pessoa",
"create_new_user": "Criar novo utilizador",
"create_shared_album_page_share_add_assets": "ADICIONAR ARQUIVOS",
"create_shared_album_page_share_add_assets": "ADICIONAR FICHEIROS",
"create_shared_album_page_share_select_photos": "Selecionar Fotos",
"create_shared_link": "Criar link partilhado",
"create_tag": "Criar etiqueta",
@@ -812,10 +812,10 @@
"delete_action_prompt": "{count} eliminados",
"delete_album": "Apagar álbum",
"delete_api_key_prompt": "Tem a certeza de que deseja remover esta chave de API?",
"delete_dialog_alert": "Esses arquivos serão permanentemente apagados do Immich e de seu dispositivo",
"delete_dialog_alert_local": "Estes arquivos serão permanentemente excluídos do seu dispositivo, mas continuarão disponíveis no servidor Immich",
"delete_dialog_alert_local_non_backed_up": "Não há backup de alguns dos arquivos no servidor e eles serão excluídos permanentemente do seu dispositivo",
"delete_dialog_alert_remote": "Estes arquivos serão permanentemente excluídos do servidor Immich",
"delete_dialog_alert": "Estes ficheiros serão eliminados permanentemente do Immich e do seu dispositivo",
"delete_dialog_alert_local": "Estes ficheiros serão eliminados permanentemente do seu dispositivo, mas continuarão disponíveis no servidor Immich",
"delete_dialog_alert_local_non_backed_up": "Alguns dos ficheiros não têm cópia de segurança no Immich e serão eliminados permanentemente do seu dispositivo",
"delete_dialog_alert_remote": "Estes ficheiros serão eliminados permanentemente do servidor Immich",
"delete_dialog_ok_force": "Confirmo que quero excluir",
"delete_dialog_title": "Excluir Permanentemente",
"delete_duplicates_confirmation": "Tem a certeza de que deseja eliminar permanentemente estes itens duplicados?",
@@ -824,7 +824,7 @@
"delete_library": "Eliminar Biblioteca",
"delete_link": "Eliminar link",
"delete_local_action_prompt": "{count} eliminados localmente",
"delete_local_dialog_ok_backed_up_only": "Excluir apenas arquivos com backup",
"delete_local_dialog_ok_backed_up_only": "Eliminar apenas ficheiros com cópia de segurança",
"delete_local_dialog_ok_force": "Excluir mesmo assim",
"delete_others": "Excluir outros",
"delete_permanently": "Eliminar permanentemente",
@@ -872,7 +872,7 @@
"download_settings_description": "Gerir definições relacionadas com a transferência de ficheiros",
"download_started": "Iniciando",
"download_sucess": "Baixado com sucesso",
"download_sucess_android": "O arquivo foi baixado na pasta DCIM/Immich",
"download_sucess_android": "O ficheiro foi descarregado para a pasta DCIM/Immich",
"download_waiting_to_retry": "Tentando novamente",
"downloading": "A transferir",
"downloading_asset_filename": "A transferir o ficheiro {filename}",
@@ -1134,7 +1134,7 @@
"group_owner": "Agrupar por dono",
"group_places_by": "Agrupar lugares por...",
"group_year": "Agrupar por ano",
"haptic_feedback_switch": "Habilitar vibração",
"haptic_feedback_switch": "Ativar vibração",
"haptic_feedback_title": "Vibração",
"has_quota": "Tem quota",
"hash_asset": "Criptografar ficheiro",
@@ -1154,20 +1154,20 @@
"hide_unnamed_people": "Ocultar pessoas sem nome",
"home_page_add_to_album_conflicts": "Foram adicionados {added} ficheiros ao álbum {album}. {failed} ficheiros já estão no álbum.",
"home_page_add_to_album_err_local": "Ainda não é possível adicionar recursos locais aos álbuns, ignorando",
"home_page_add_to_album_success": "Adicionado {added} arquivos ao álbum {album}.",
"home_page_album_err_partner": "Ainda não é possível adicionar arquivos do parceiro a um álbum, ignorando",
"home_page_add_to_album_success": "{added} ficheiros foram adicionados ao álbum {album}.",
"home_page_album_err_partner": "Ainda não é possível adicionar ficheiros do parceiro a um álbum, a ignorar",
"home_page_archive_err_local": "Ainda não é possível arquivar recursos locais, ignorando",
"home_page_archive_err_partner": "Não é possível arquivar Fotos e Videos do parceiro, ignorando",
"home_page_building_timeline": "Construindo a linha do tempo",
"home_page_delete_err_partner": "Não é possível excluir arquivos do parceiro, ignorando",
"home_page_delete_remote_err_local": "Foram selecionados arquivos locais para excluir remotamente, ignorando",
"home_page_delete_err_partner": "Não é possível eliminar ficheiros do parceiro, a ignorar",
"home_page_delete_remote_err_local": "Foram selecionados ficheiros locais para excluir remotamente, a ignorar",
"home_page_favorite_err_local": "Ainda não é possível adicionar recursos locais favoritos, ignorando",
"home_page_favorite_err_partner": "Ainda não é possível marcar arquivos do parceiro como favoritos, ignorando",
"home_page_favorite_err_partner": "Ainda não é possível marcar ficheiros do parceiro como favoritos, a ignorar",
"home_page_first_time_notice": "Se é a primeira vez que utiliza a aplicação, certifique-se de que marca pelo menos um álbum do dispositivo para cópia de segurança, para a linha do tempo poder ser preenchida com fotos e vídeos",
"home_page_locked_error_local": "Não foi possível mover ficheiros locais para a pasta trancada, a continuar",
"home_page_locked_error_partner": "Não foi possível mover ficheiros do parceiro para a pasta trancada, a continuar",
"home_page_share_err_local": "Não é possível compartilhar arquivos locais com um link, ignorando",
"home_page_upload_err_limit": "Só é possível enviar 30 arquivos por vez, ignorando",
"home_page_share_err_local": "Não é possível partilhar ficheiros locais com um link, a ignorar",
"home_page_upload_err_limit": "Só é possível enviar um máximo de 30 ficheiros de cada vez, a ignorar",
"host": "Servidor",
"hour": "Hora",
"hours": "Horas",
@@ -1187,7 +1187,7 @@
"image_alt_text_date_place_3_people": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} em {city}, {country} com {person1}, {person2}, e {person3} em {date}",
"image_alt_text_date_place_4_or_more_people": "{isVideo, select, true {Vídeo gravado} other {Foto tirada}} em {city}, {country} com {person1}, {person2}, e outras {additionalCount, number} pessoas em {date}",
"image_saved_successfully": "Imagem salva",
"image_viewer_page_state_provider_download_started": "Baixando arquivo",
"image_viewer_page_state_provider_download_started": "A descarregar ficheiro",
"image_viewer_page_state_provider_download_success": "Baixado com sucesso",
"image_viewer_page_state_provider_share_error": "Erro ao compartilhar",
"immich_logo": "Logotipo do Immich",
@@ -1244,7 +1244,7 @@
"library_options": "Opções da biblioteca",
"library_page_device_albums": "Álbuns no dispositivo",
"library_page_new_album": "Novo álbum",
"library_page_sort_asset_count": "Quantidade de arquivos",
"library_page_sort_asset_count": "Quantidade de ficheiros",
"library_page_sort_created": "Data de criação",
"library_page_sort_last_modified": "Última modificação",
"library_page_sort_title": "Título do álbum",
@@ -1383,8 +1383,8 @@
"moved_to_archive": "{count, plural, one {Foi movido # ficheiro} other {Foram movidos # ficheiros}} para o arquivo",
"moved_to_library": "{count, plural, one {Foi movido # ficheiro} other {Foram movidos # ficheiros}} para a biblioteca",
"moved_to_trash": "Enviado para a reciclagem",
"multiselect_grid_edit_date_time_err_read_only": "Não é possível editar a data de arquivo só leitura, ignorando",
"multiselect_grid_edit_gps_err_read_only": "Não é possível editar a localização de arquivo só leitura, ignorando",
"multiselect_grid_edit_date_time_err_read_only": "Não é possível editar a data de um ficheiro só de leitura, a ignorar",
"multiselect_grid_edit_gps_err_read_only": "Não é possível editar a localização de um ficheiro só de leitura, a ignorar",
"mute_memories": "Silenciar Memórias",
"my_albums": "Os meus álbuns",
"name": "Nome",
@@ -1417,7 +1417,7 @@
"no_albums_yet": "Parece que ainda não tem nenhum álbum.",
"no_archived_assets_message": "Arquive fotos e vídeos para os ocultar da sua visualização de fotos",
"no_assets_message": "FAÇA CLIQUE PARA CARREGAR A SUA PRIMEIRA FOTO",
"no_assets_to_show": "Não há arquivos para exibir",
"no_assets_to_show": "Não há ficheiros para exibir",
"no_cast_devices_found": "Nenhum dispositivo de transmissão encontrado",
"no_checksum_local": "Sem cálculo de verificação disponível - não pode capturar conteúdos locais",
"no_checksum_remote": "Soma de verificação (checksum) não disponível - não é possível obter o recurso remoto",
@@ -1586,7 +1586,7 @@
"public_album": "Álbum público",
"public_share": "Partilhar Publicamente",
"purchase_account_info": "Apoiante",
"purchase_activated_subtitle": "Agradecemos por apoiar o Immich e software de código aberto",
"purchase_activated_subtitle": "Agradecemos o seu apoio ao Immich e ao software de código aberto",
"purchase_activated_time": "Ativado em {date}",
"purchase_activated_title": "A sua chave foi ativada com sucesso",
"purchase_button_activate": "Ativar",
@@ -1747,7 +1747,7 @@
"search_filter_date_title": "Selecione a data",
"search_filter_display_option_not_in_album": "Fora de álbum",
"search_filter_display_options": "Opções de exibição",
"search_filter_filename": "Pesquisar por nome do arquivo",
"search_filter_filename": "Pesquisar por nome do ficheiro",
"search_filter_location": "Localização",
"search_filter_location_title": "Selecione a localização",
"search_filter_media_type": "Tipo da mídia",
@@ -1839,15 +1839,15 @@
"setting_notifications_notify_minutes": "{count} minutos",
"setting_notifications_notify_never": "Nunca",
"setting_notifications_notify_seconds": "{count} segundos",
"setting_notifications_single_progress_subtitle": "Informações detalhadas sobre o progresso do envio por arquivo",
"setting_notifications_single_progress_subtitle": "Informações detalhadas sobre o progresso do envio por ficheiro",
"setting_notifications_single_progress_title": "Mostrar progresso detalhado do backup em segundo plano",
"setting_notifications_subtitle": "Ajuste as preferências de notificação",
"setting_notifications_total_progress_subtitle": "Progresso do envio de arquivos (concluídos/total)",
"setting_notifications_total_progress_subtitle": "Progresso do envio de ficheiro (concluídos/total)",
"setting_notifications_total_progress_title": "Mostrar progresso total do backup em segundo plano",
"setting_video_viewer_auto_play_subtitle": "Reproduzir os vídeos automaticamente quando abertos",
"setting_video_viewer_auto_play_title": "Reproduzir vídeos automaticamente",
"setting_video_viewer_looping_title": "Repetir",
"setting_video_viewer_original_video_subtitle": "Ao transmitir um vídeo do servidor, usar o arquivo original, mesmo quando uma versão transcodificada esteja disponível. Pode fazer com que o vídeo demore para carregar. Vídeos disponíveis localmente são exibidos na qualidade original independente desta configuração.",
"setting_video_viewer_original_video_subtitle": "Ao transmitir um vídeo do servidor, usar o ficheiro original, mesmo se uma versão transcodificada estiver disponível. Pode causar interrupções. Vídeos disponíveis localmente são exibidos na qualidade original independentemente desta definição.",
"setting_video_viewer_original_video_title": "Forçar vídeo original",
"settings": "Definições",
"settings_require_restart": "Reinicie o Immich para aplicar essa configuração",
@@ -2019,7 +2019,7 @@
"theme_setting_primary_color_subtitle": "Selecione a cor primária, utilizada nas ações principais e nos realces.",
"theme_setting_primary_color_title": "Cor primária",
"theme_setting_system_primary_color_title": "Use a cor do sistema",
"theme_setting_system_theme_switch": "Automático (Siga a configuração do sistema)",
"theme_setting_system_theme_switch": "Automático (Seguir a configuração do sistema)",
"theme_setting_theme_subtitle": "Escolha a configuração do tema da aplicação",
"theme_setting_three_stage_loading_subtitle": "O carregamento em três estágios pode aumentar o desempenho do carregamento, mas causa uma carga de rede significativamente maior",
"theme_setting_three_stage_loading_title": "Habilitar carregamento em três estágios",
@@ -2048,11 +2048,11 @@
"trash_emptied": "Lixeira esvaziada",
"trash_no_results_message": "Fotos e vídeos enviados para a reciclagem aparecem aqui.",
"trash_page_delete_all": "Excluir tudo",
"trash_page_empty_trash_dialog_content": "Deseja esvaziar a lixera? Estes arquivos serão apagados de forma permanente do Immich",
"trash_page_empty_trash_dialog_content": "Deseja esvaziar a reciclagem? Estes ficheiros serão apagados permanentemente do Immich",
"trash_page_info": "Ficheiros na reciclagem irão ser eliminados permanentemente após {days} dias",
"trash_page_no_assets": "Lixeira vazia",
"trash_page_restore_all": "Restaurar tudo",
"trash_page_select_assets_btn": "Selecionar arquivos",
"trash_page_select_assets_btn": "Selecionar ficheiros",
"trash_page_title": "Reciclagem ({count})",
"trashed_items_will_be_permanently_deleted_after": "Os itens da reciclagem são eliminados permanentemente após {days, plural, one {# dia} other {# dias}}.",
"troubleshoot": "Diagnosticar problemas",
@@ -2094,8 +2094,8 @@
"upload_action_prompt": "{count} à espera de carregar",
"upload_concurrency": "Carregamentos em simultâneo",
"upload_details": "Detalhes do Carregamento",
"upload_dialog_info": "Deseja fazer o backup dos arquivos selecionados no servidor?",
"upload_dialog_title": "Enviar arquivo",
"upload_dialog_info": "Deseja realizar uma cópia de segurança dos ficheiros selecionados para o servidor?",
"upload_dialog_title": "Enviar ficheiro",
"upload_errors": "Envio completo com {count, plural, one {# erro} other {# erros}}, atualize a página para ver os novos ficheiros enviados.",
"upload_finished": "Carregamento acabado",
"upload_progress": "Restante(s) {remaining, number} - Processado(s) {processed, number}/{total, number}",

View File

@@ -485,7 +485,7 @@
"app_bar_signout_dialog_content": "Вы уверены, что хотите выйти?",
"app_bar_signout_dialog_ok": "Да",
"app_bar_signout_dialog_title": "Выйти",
"app_download_links": "Загрузка приложения",
"app_download_links": "Ссылки на загрузку мобильного приложения",
"app_settings": "Параметры приложения",
"app_stores": "Магазины приложений",
"app_update_available": "Доступна новая версия приложения",
@@ -1346,7 +1346,7 @@
"map_zoom_to_see_photos": "Уменьшение масштаба для просмотра фотографий",
"mark_all_as_read": "Прочитано",
"mark_as_read": "Отметить как прочитанное",
"marked_all_as_read": "Отмечены как прочитанные",
"marked_all_as_read": "Все уведомления отмечены как прочитанные",
"matches": "Совпадения",
"matching_assets": "Соответствующие объекты",
"media_type": "Тип медиа",
@@ -1453,7 +1453,7 @@
"oauth": "OAuth",
"obtainium_configurator": "Настройка Obtainium",
"obtainium_configurator_instructions": "Для установки и обновления Android приложения Immich напрямую из источников на GitHub (минуя магазины приложений) можно использовать Obtainium. Создайте новый API ключ и укажите архитектуру приложения для формирования ссылки для Obtainium.",
"ocr": "Распознавание текста",
"ocr": "OCR",
"official_immich_resources": "Официальные ресурсы Immich",
"offline": "Недоступен",
"offset": "Смещение",
@@ -1858,7 +1858,7 @@
"share_add_photos": "Добавить фото",
"share_assets_selected": "{count} выбрано",
"share_dialog_preparing": "Подготовка...",
"share_link": "Поделиться ссылкой",
"share_link": "Создать ссылку",
"shared": "Общиe",
"shared_album_activities_input_disable": "Комментарии отключены",
"shared_album_activity_remove_content": "Удалить сообщение?",

View File

@@ -418,7 +418,7 @@
"advanced_settings_prefer_remote_title": "Föredra bilder från servern",
"advanced_settings_proxy_headers_subtitle": "Definiera proxy-headers som Immich ska skicka med i varje närverksanrop",
"advanced_settings_proxy_headers_title": "Anpassade proxyheaders [EXPERIMENTELLT]",
"advanced_settings_readonly_mode_subtitle": "Aktiverar skrivskyddat läge där foton endast kan visas. Följande funktioner inaktiveras: välj flera bilder, dela, casta, ta bort bilder. Aktivera/inaktivera skrivskyddat läge via profilbilden på appens hemskärm",
"advanced_settings_readonly_mode_subtitle": "Aktiverar skrivskyddat-läge där foton endast kan visas. Följande funktioner inaktiveras: välj flera bilder, dela, casta, ta bort bilder. Aktivera/inaktivera skrivskyddat läge via profilbilden på appens hemskärm",
"advanced_settings_readonly_mode_title": "Skrivskyddat läge",
"advanced_settings_self_signed_ssl_subtitle": "Hoppar över verifiering av serverns SSL-certifikat. Krävs för självsignerade certifikat.",
"advanced_settings_self_signed_ssl_title": "Tillåt självsignerade SSL-certifikat [EXPERIMENTELLT]",
@@ -791,6 +791,7 @@
"daily_title_text_date_year": "E, dd MMM, yyyy",
"dark": "Mörk",
"dark_theme": "Växla mörkt tema",
"date": "Datum",
"date_after": "Datum efter",
"date_and_time": "Datum och Tid",
"date_before": "Datum före",
@@ -1099,6 +1100,7 @@
"features_setting_description": "Hantera appens funktioner",
"file_name": "Filnamn",
"file_name_or_extension": "Filnamn eller -tillägg",
"file_size": "Filstorlek",
"filename": "Filnamn",
"filetype": "Filtyp",
"filter": "Filter",
@@ -1262,6 +1264,7 @@
"local_media_summary": "Sammanfattning av lokala medier",
"local_network": "Lokalt nätverk",
"local_network_sheet_info": "Appen kommer ansluta till servern via denna URL när det specificerade WiFi-nätverket används",
"location": "Position",
"location_permission": "Plats-rättighet",
"location_permission_content": "För att använda funktionen för automatisk växling behöver Immich behörighet till exakt plats så att appen kan läsa av det aktuella Wi-Fi-nätverkets namn",
"location_picker_choose_on_map": "Välj på karta",
@@ -1694,6 +1697,7 @@
"reset_sqlite_confirmation": "Är du säker på att du vill återställa SQLite-databasen? Du måste logga ut och logga in igen för att synkronisera om data",
"reset_sqlite_success": "Återställde SQLite-databasen",
"reset_to_default": "Återställ till standard",
"resolution": "Upplösning",
"resolve_duplicates": "Lös dubletter",
"resolved_all_duplicates": "Lös alla dubletter",
"restore": "Återställ",
@@ -1712,6 +1716,7 @@
"running": "Igångsatt",
"save": "Spara",
"save_to_gallery": "Spara i galleri",
"saved": "Sparad",
"saved_api_key": "Sparad API-nyckel",
"saved_profile": "Sparade profil",
"saved_settings": "Sparade inställningar",
@@ -2020,6 +2025,7 @@
"theme_setting_three_stage_loading_title": "Aktivera trestegsladdning",
"they_will_be_merged_together": "De kommer att slås samman",
"third_party_resources": "Tredjepartsresurser",
"time": "Tid",
"time_based_memories": "Tidsbaserade minnen",
"timeline": "Tidslinje",
"timezone": "Tidszon",

View File

@@ -154,6 +154,18 @@
"machine_learning_min_detection_score_description": "ஒரு முகம் 0-1 முதல் கண்டறியப்படுவதற்கு குறைந்தபட்ச நம்பிக்கை மதிப்பெண். குறைந்த மதிப்புகள் அதிக முகங்களைக் கண்டறியும், ஆனால் தவறான நேர்மறைகளை ஏற்படுத்தக்கூடும்.",
"machine_learning_min_recognized_faces": "குறைந்தபட்ச அங்கீகரிக்கப்பட்ட முகங்கள்",
"machine_learning_min_recognized_faces_description": "ஒரு நபருக்கு உருவாக்கப்பட வேண்டிய அங்கீகரிக்கப்பட்ட முகங்களின் குறைந்தபட்ச எண்ணிக்கை. இதை அதிகரிப்பது, ஒரு நபருக்கு முகம் ஒதுக்கப்படாமல் போகும் வாய்ப்பை அதிகரிக்கும் செலவில், முக அங்கீகாரத்தை மிகவும் துல்லியமாக்குகிறது.",
"machine_learning_ocr": "ஓசிஆர்",
"machine_learning_ocr_description": "படங்களில் உள்ள உரையை அடையாளம் காண இயந்திர கற்றலைப் பயன்படுத்தவும்",
"machine_learning_ocr_enabled": "ஓசிஆர் ஐ இயலச்செய்",
"machine_learning_ocr_enabled_description": "முடக்கப்பட்டால், படங்கள் உரை அடையாளம் காணப்படாது.",
"machine_learning_ocr_max_resolution": "அதிகபட்ச தெளிவுத்திறன்",
"machine_learning_ocr_max_resolution_description": "இந்தத் தெளிவுத்திறனுக்கு மேலே உள்ள மாதிரிக்காட்சிகள், தோற்ற விகிதத்தைப் பாதுகாக்கும் அதே வேளையில், அளவு மாற்றப்படும். அதிக மதிப்புகள் மிகவும் துல்லியமானவை, ஆனால் செயலாக்கவும் அதிக நினைவகத்தைப் பயன்படுத்தவும் அதிக நேரம் எடுக்கும்.",
"machine_learning_ocr_min_detection_score": "குறைந்தபட்ச கண்டறிதல் மதிப்பெண்",
"machine_learning_ocr_min_detection_score_description": "உரையைக் கண்டறிய குறைந்தபட்ச நம்பிக்கை மதிப்பெண் 0-1. குறைந்த மதிப்பெண் அதிக உரையைக் கண்டறியும், ஆனால் தவறான தகவல்க்கு வழிவகுக்கும்.",
"machine_learning_ocr_min_recognition_score": "குறைந்தபட்ச அங்கீகார மதிப்பெண்",
"machine_learning_ocr_min_score_recognition_description": "கண்டறியப்பட்ட உரையை அங்கீகரிக்க குறைந்தபட்ச நம்பிக்கை மதிப்பெண் 0-1 ஆகும். குறைந்த மதிப்பெண் அதிக உரையை அங்கீகரிக்கும், ஆனால் தவறான நேர்மறைகளுக்கு வழிவகுக்கும்.",
"machine_learning_ocr_model": "ஓசிஆர் மாதிரி",
"machine_learning_ocr_model_description": "மொபைல் மாடல்களை விட சர்வர் மாடல்கள் மிகவும் துல்லியமானவை, ஆனால் அதிக நினைவகத்தை செயலாக்கி பயன்படுத்த அதிக நேரம் எடுக்கும்.",
"machine_learning_settings": "இயந்திர கற்றல் அமைப்புகள்",
"machine_learning_settings_description": "இயந்திர கற்றல் அம்சங்கள் மற்றும் அமைப்புகளை நிர்வகிக்கவும்",
"machine_learning_smart_search": "ஸ்மார்ட் தேடல்",
@@ -245,6 +257,7 @@
"oauth_storage_quota_default_description": "GiB இல் உள்ள ஒதுக்கீடு எந்த உரிமைகோரலும் வழங்கப்படாதபோது பயன்படுத்தப்படும் .",
"oauth_timeout": "கோரிக்கை நேரம் முடிந்தது",
"oauth_timeout_description": "கோரிக்கைகளுக்கான காலக்கெடு மில்லி வினாடிகளில்",
"ocr_job_description": "படங்களில் உள்ள உரையை அடையாளம் காண இயந்திர கற்றலைப் பயன்படுத்தவும்",
"password_enable_description": "மின்னஞ்சல் மற்றும் கடவுச்சொல் மூலம் உள்நுழையவும்",
"password_settings": "கடவுச்சொல் உள்நுழைவு",
"password_settings_description": "கடவுச்சொல் உள்நுழைவு அமைப்புகளை நிர்வகிக்கவும்",
@@ -669,6 +682,8 @@
"change_password_description": "நீங்கள் கணினியில் கையொப்பமிடுவது இதுவே முதல் முறை அல்லது உங்கள் கடவுச்சொல்லை மாற்றுவதற்கான கோரிக்கை செய்யப்பட்டுள்ளது. கீழே புதிய கடவுச்சொல்லை உள்ளிடவும்.",
"change_password_form_confirm_password": "கடவுச்சொல்லை உறுதிப்படுத்தவும்",
"change_password_form_description": "ஆய் {name}, \n\nநீங்கள் கணினியில் கையொப்பமிடுவது இதுவே முதல் முறை அல்லது உங்கள் கடவுச்சொல்லை மாற்றுவதற்கான கோரிக்கை செய்யப்பட்டுள்ளது. கீழே புதிய கடவுச்சொல்லை உள்ளிடவும்.",
"change_password_form_log_out": "மற்ற எல்லா சாதனங்களிலிருந்தும் வெளியேறு",
"change_password_form_log_out_description": "மற்ற எல்லா சாதனங்களிலிருந்தும் வெளியேற பரிந்துரைக்கப்படுகிறது",
"change_password_form_new_password": "புதிய கடவுச்சொல்",
"change_password_form_password_mismatch": "கடவுச்சொற்கள் பொருந்தவில்லை",
"change_password_form_reenter_new_password": "புதிய கடவுச்சொல்லை மீண்டும் உள்ளிடவும்",
@@ -776,6 +791,7 @@
"daily_title_text_date_year": "E, mmm dd, yyyy",
"dark": "இருண்ட",
"dark_theme": "இருண்ட கருப்பொருளை மாற்றவும்",
"date": "தேதி",
"date_after": "தேதி",
"date_and_time": "தேதி மற்றும் நேரம்",
"date_before": "முன் தேதி",
@@ -1084,6 +1100,7 @@
"features_setting_description": "பயன்பாட்டு அம்சங்களை நிர்வகிக்கவும்",
"file_name": "கோப்பு பெயர்",
"file_name_or_extension": "கோப்பு பெயர் அல்லது நீட்டிப்பு",
"file_size": "கோப்பு அளவு",
"filename": "கோப்புப்பெயர்",
"filetype": "பைல்டைப்",
"filter": "வடிப்பி",
@@ -1247,6 +1264,7 @@
"local_media_summary": "உள்ளக ஊடக சுருக்கம்",
"local_network": "உள்ளக பிணையம்",
"local_network_sheet_info": "குறிப்பிட்ட வைஃபை நெட்வொர்க்கைப் பயன்படுத்தும் போது பயன்பாடு இந்த முகவரி மூலம் சேவையகத்துடன் இணைக்கப்படும்",
"location": "இடம்",
"location_permission": "இருப்பிட இசைவு",
"location_permission_content": "ஆட்டோ-ச்விட்சிங் அம்சத்தைப் பயன்படுத்த, இம்மிக்கு துல்லியமான இருப்பிட இசைவு தேவை, எனவே இது தற்போதைய வைஃபை நெட்வொர்க்கின் பெயரைப் படிக்க முடியும்",
"location_picker_choose_on_map": "வரைபடத்தில் தேர்வு செய்யவும்",
@@ -1435,6 +1453,7 @@
"oauth": "Oauth",
"obtainium_configurator": "ஒப்டெய்னியம் கட்டமைப்பாளர்",
"obtainium_configurator_instructions": "Immich GitHub வெளியீட்டில் இருந்து நேரடியாக ஆண்ட்ராய்டு பயன்பாட்டை நிறுவவும் புதுப்பிக்கவும் Obtainium ஐப் பயன்படுத்தவும். பநிஇ விசையை உருவாக்கி, உங்கள் ஒப்டெய்னியம் உள்ளமைவு இணைப்பை உருவாக்க ஒரு மாறுபாட்டைத் தேர்ந்தெடுக்கவும்",
"ocr": "ஓசிஆர்",
"official_immich_resources": "உத்தியோகபூர்வ இம்மா வளங்கள்",
"offline": "இணையமில்லாமல்",
"offset": "ஈடுசெய்யும்",
@@ -1678,6 +1697,7 @@
"reset_sqlite_confirmation": "SQLITE தரவுத்தளத்தை மீட்டமைக்க விரும்புகிறீர்களா? தரவை மீண்டும் ஒத்திசைக்க நீங்கள் வெளியேறி மீண்டும் உள்நுழைய வேண்டும்",
"reset_sqlite_success": "SQLITE தரவுத்தளத்தை வெற்றிகரமாக மீட்டமைக்கவும்",
"reset_to_default": "இயல்புநிலைக்கு மீட்டமைக்கவும்",
"resolution": "தெளிவுத்திறன்",
"resolve_duplicates": "நகல்களைத் தீர்க்கவும்",
"resolved_all_duplicates": "அனைத்து நகல்களையும் தீர்க்கும்",
"restore": "மீட்டமை",
@@ -1696,6 +1716,7 @@
"running": "இயங்கும்",
"save": "சேமி",
"save_to_gallery": "கேலரியில் சேமிக்கவும்",
"saved": "சேமிக்கப்பட்டது",
"saved_api_key": "சேமித்த பநிஇ விசை",
"saved_profile": "சேமித்த சுயவிவரம்",
"saved_settings": "சேமித்த அமைப்புகள்",
@@ -1712,6 +1733,8 @@
"search_by_description_example": "சப்பாவில் நடைபயணம்",
"search_by_filename": "கோப்பு பெயர் அல்லது நீட்டிப்பு மூலம் தேடுங்கள்",
"search_by_filename_example": "I.E. IMG_1234.JPG அல்லது PNG",
"search_by_ocr": "ஓசிஆர் மூலம் தேடு",
"search_by_ocr_example": "லேட்",
"search_camera_lens_model": "கண்ணாடி வில்லை மாதிரியைத் தேடு...",
"search_camera_make": "தேடல் கேமரா செய்யுங்கள் ...",
"search_camera_model": "கேமரா மாதிரியைத் தேடுங்கள் ...",
@@ -1729,6 +1752,7 @@
"search_filter_location_title": "இருப்பிடத்தைத் தேர்ந்தெடுக்கவும்",
"search_filter_media_type": "ஊடக வகை",
"search_filter_media_type_title": "மீடியா வகையைத் தேர்ந்தெடுக்கவும்",
"search_filter_ocr": "ஓசிஆர் மூலம் தேடு",
"search_filter_people_title": "மக்களைத் தேர்ந்தெடுக்கவும்",
"search_for": "தேடுங்கள்",
"search_for_existing_person": "இருக்கும் நபரைத் தேடுங்கள்",
@@ -2001,6 +2025,7 @@
"theme_setting_three_stage_loading_title": "மூன்று-நிலை ஏற்றுதலை இயக்கவும்",
"they_will_be_merged_together": "அவர்கள் ஒன்றாக இணைக்கப்படுவார்கள்",
"third_party_resources": "மூன்றாம் தரப்பு வளங்கள்",
"time": "நேரம்",
"time_based_memories": "நேர அடிப்படையிலான நினைவுகள்",
"timeline": "காலவரிசை",
"timezone": "நேர மண்டலம்",

View File

@@ -163,6 +163,7 @@
"machine_learning_ocr_min_detection_score": "En düşük tespit puanı",
"machine_learning_ocr_min_detection_score_description": "Metnin tespit edilmesi için minimum güven puanı 0-1 arasındadır. Düşük değerler daha fazla metin tespit eder, ancak yanlış pozitif sonuçlara yol açabilir.",
"machine_learning_ocr_min_recognition_score": "Minimum tespit puanı",
"machine_learning_ocr_model": "OCR modeli",
"machine_learning_settings": "Makine Öğrenmesi ayarları",
"machine_learning_settings_description": "Makine öğrenmesi özelliklerini ve ayarlarını yönet",
"machine_learning_smart_search": "Akıllı Arama",
@@ -785,6 +786,7 @@
"daily_title_text_date_year": "dd MMM yyyy E",
"dark": "Koyu",
"dark_theme": "Karanlık temaya geç",
"date": "Tarih",
"date_after": "Sonraki tarih",
"date_and_time": "Tarih ve Zaman",
"date_before": "Önceki tarih",
@@ -1093,6 +1095,7 @@
"features_setting_description": "Uygulamanın özelliklerini yönet",
"file_name": "Dosya adı",
"file_name_or_extension": "Dosya adı veya uzantı",
"file_size": "Dosya boyutu",
"filename": "Dosya adı",
"filetype": "Dosya tipi",
"filter": "Filtre",
@@ -1444,6 +1447,7 @@
"oauth": "OAuth",
"obtainium_configurator": "Obtainium Yapılandırıcı",
"obtainium_configurator_instructions": "Obtainium kullanarak Android uygulamasını doğrudan Immich GitHub sürümünden yükleyin ve güncelleyin. Bir API anahtarı oluşturun ve bir varyant seçerek Obtainium yapılandırma bağlantınızı oluşturun",
"ocr": "OCR",
"official_immich_resources": "Resmi Immich Kaynakları",
"offline": "Çevrim dışı",
"offset": "Ofset",
@@ -1687,6 +1691,7 @@
"reset_sqlite_confirmation": "SQLite veritabanını sıfırlamak istediğinizden emin misiniz? Verileri yeniden eşzamanlamak için oturumu kapatıp tekrar oturum açmanız gerekecektir",
"reset_sqlite_success": "SQLite veritabanını başarıyla sıfırladınız",
"reset_to_default": "Varsayılana sıfırla",
"resolution": "Çözünürlük",
"resolve_duplicates": "Çiftleri çöz",
"resolved_all_duplicates": "Tüm çiftler çözüldü",
"restore": "Geri yükle",
@@ -2010,6 +2015,7 @@
"theme_setting_three_stage_loading_title": "Üç aşamalı yüklemeyi etkinleştir",
"they_will_be_merged_together": "Birlikte birleştirilecekler",
"third_party_resources": "Üçüncü taraf kaynaklar",
"time": "Zaman",
"time_based_memories": "Zaman bazlı anılar",
"timeline": "Zaman Çizelgesi",
"timezone": "Zaman dilimi",

View File

@@ -154,6 +154,8 @@
"machine_learning_min_detection_score_description": "臉孔偵測的最低信心分數,範圍為 0 至 1。數值較低時會偵測到更多臉孔但可能導致誤判。",
"machine_learning_min_recognized_faces": "最低臉部辨識數量",
"machine_learning_min_recognized_faces_description": "建立新人物所需的最低已辨識臉孔數量。提高此數值可讓臉孔辨識更精確,但同時會增加臉孔未被指派給任何人物的可能性。",
"machine_learning_ocr": "文字辨識(OCR)",
"machine_learning_ocr_description": "使用機器學習來識別圖片中的文字",
"machine_learning_settings": "機器學習設定",
"machine_learning_settings_description": "管理機器學習的功能和設定",
"machine_learning_smart_search": "智慧搜尋",
@@ -245,6 +247,7 @@
"oauth_storage_quota_default_description": "未提供宣告時所使用的配額GiB。",
"oauth_timeout": "請求逾時",
"oauth_timeout_description": "請求的逾時時間(毫秒)",
"ocr_job_description": "使用機器學習來識別圖像中的文字",
"password_enable_description": "使用電子郵件和密碼登入",
"password_settings": "密碼登入",
"password_settings_description": "管理密碼登入設定",

View File

@@ -10,6 +10,7 @@ from rapidocr.inference_engine.base import FileInfo, InferSession
from rapidocr.utils import DownloadFile, DownloadFileInput
from rapidocr.utils.typings import EngineType, LangRec, OCRVersion, TaskType
from rapidocr.utils.typings import ModelType as RapidModelType
from rapidocr.utils.vis_res import VisRes
from immich_ml.config import log, settings
from immich_ml.models.base import InferenceModel
@@ -31,6 +32,7 @@ class TextRecognizer(InferenceModel):
"text": [],
"textScore": np.empty(0, dtype=np.float32),
}
VisRes.__init__ = lambda self, **kwargs: None # pyright: ignore[reportAttributeAccessIssue]
super().__init__(model_name, **model_kwargs, model_format=ModelFormat.ONNX)
def _download(self) -> None:

View File

@@ -1,6 +1,6 @@
[project]
name = "immich-ml"
version = "2.2.0"
version = "2.2.2"
description = ""
authors = [{ name = "Hau Tran", email = "alex.tran1502@gmail.com" }]
requires-python = ">=3.10,<4.0"
@@ -22,7 +22,6 @@ dependencies = [
"rich>=13.4.2",
"tokenizers>=0.15.0,<1.0",
"uvicorn[standard]>=0.22.0,<1.0",
"setuptools>=78.1.0",
"rapidocr>=3.1.0",
]

View File

@@ -1100,7 +1100,6 @@ dependencies = [
{ name = "python-multipart" },
{ name = "rapidocr" },
{ name = "rich" },
{ name = "setuptools" },
{ name = "tokenizers" },
{ name = "uvicorn", extra = ["standard"] },
]
@@ -1188,7 +1187,6 @@ requires-dist = [
{ name = "rapidocr", specifier = ">=3.1.0" },
{ name = "rich", specifier = ">=13.4.2" },
{ name = "rknn-toolkit-lite2", marker = "extra == 'rknn'", specifier = ">=2.3.0,<3" },
{ name = "setuptools", specifier = ">=78.1.0" },
{ name = "tokenizers", specifier = ">=0.15.0,<1.0" },
{ name = "uvicorn", extras = ["standard"], specifier = ">=0.22.0,<1.0" },
]

View File

@@ -35,8 +35,8 @@ platform :android do
task: 'bundle',
build_type: 'Release',
properties: {
"android.injected.version.code" => 3023,
"android.injected.version.name" => "2.2.0",
"android.injected.version.code" => 3025,
"android.injected.version.name" => "2.2.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')

View File

@@ -1,4 +1,5 @@
source "https://rubygems.org"
gem "fastlane"
gem "cocoapods"
gem "cocoapods"
gem "abbrev" # Required for Ruby 3.4+

View File

@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objectVersion = 77;
objects = {
/* Begin PBXBuildFile section */
@@ -714,7 +714,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 231;
CURRENT_PROJECT_VERSION = 233;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO;
@@ -858,7 +858,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 231;
CURRENT_PROJECT_VERSION = 233;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO;
@@ -888,7 +888,7 @@
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 231;
CURRENT_PROJECT_VERSION = 233;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_BITCODE = NO;
@@ -922,7 +922,7 @@
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 231;
CURRENT_PROJECT_VERSION = 233;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -965,7 +965,7 @@
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 231;
CURRENT_PROJECT_VERSION = 233;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -1005,7 +1005,7 @@
CODE_SIGN_ENTITLEMENTS = WidgetExtension/WidgetExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 231;
CURRENT_PROJECT_VERSION = 233;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -1044,7 +1044,7 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 231;
CURRENT_PROJECT_VERSION = 233;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
@@ -1088,7 +1088,7 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 231;
CURRENT_PROJECT_VERSION = 233;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
@@ -1129,7 +1129,7 @@
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 231;
CURRENT_PROJECT_VERSION = 233;
CUSTOM_GROUP_ID = group.app.immich.share;
DEVELOPMENT_TEAM = 2F67MQ8R79;
ENABLE_USER_SCRIPT_SANDBOXING = YES;

View File

@@ -80,7 +80,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>2.1.0</string>
<string>2.2.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@@ -107,7 +107,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>231</string>
<string>233</string>
<key>FLTEnableImpeller</key>
<true/>
<key>ITSAppUsesNonExemptEncryption</key>

View File

@@ -16,42 +16,81 @@
default_platform(:ios)
platform :ios do
desc "iOS Release to TestFlight"
lane :release_ci do
# Setup CI environment
setup_ci
# Load App Store Connect API Key
api_key = app_store_connect_api_key(
# Constants
TEAM_ID = "2F67MQ8R79"
CODE_SIGN_IDENTITY = "Apple Distribution: Hau Tran (#{TEAM_ID})"
BASE_BUNDLE_ID = "app.alextran.immich"
# Helper method to get App Store Connect API key
def get_api_key
app_store_connect_api_key(
key_id: ENV["APP_STORE_CONNECT_API_KEY_ID"],
issuer_id: ENV["APP_STORE_CONNECT_API_KEY_ISSUER_ID"],
key_filepath: "api_key.json"
key_filepath: "#{Dir.home}/.appstoreconnect/private_keys/AuthKey_#{ENV['APP_STORE_CONNECT_API_KEY_ID']}.p8",
duration: 1200,
in_house: false
)
end
# Helper method to configure code signing for all targets
def configure_code_signing(bundle_id_suffix: "")
bundle_suffix = bundle_id_suffix.empty? ? "" : ".#{bundle_id_suffix}"
# Import certificate and provisioning profile
import_certificate(
certificate_path: "certificate.p12",
certificate_password: ENV["IOS_CERTIFICATE_PASSWORD"],
keychain_name: ENV["KEYCHAIN_NAME"],
keychain_password: ENV["KEYCHAIN_PASSWORD"]
)
# Install provisioning profile
install_provisioning_profile(path: "profile.mobileprovision")
# Configure code signing
# Runner (main app)
update_code_signing_settings(
use_automatic_signing: false,
path: "./Runner.xcodeproj",
team_id: ENV["FASTLANE_TEAM_ID"],
profile_name: "app.alextran.immich AppStore"
team_id: ENV["FASTLANE_TEAM_ID"] || TEAM_ID,
code_sign_identity: CODE_SIGN_IDENTITY,
bundle_identifier: "#{BASE_BUNDLE_ID}#{bundle_suffix}",
profile_name: "#{BASE_BUNDLE_ID}#{bundle_suffix} AppStore",
targets: ["Runner"]
)
# ShareExtension
update_code_signing_settings(
use_automatic_signing: false,
path: "./Runner.xcodeproj",
team_id: ENV["FASTLANE_TEAM_ID"] || TEAM_ID,
code_sign_identity: CODE_SIGN_IDENTITY,
bundle_identifier: "#{BASE_BUNDLE_ID}#{bundle_suffix}.ShareExtension",
profile_name: "#{BASE_BUNDLE_ID}#{bundle_suffix}.ShareExtension AppStore",
targets: ["ShareExtension"]
)
# WidgetExtension
update_code_signing_settings(
use_automatic_signing: false,
path: "./Runner.xcodeproj",
team_id: ENV["FASTLANE_TEAM_ID"] || TEAM_ID,
code_sign_identity: CODE_SIGN_IDENTITY,
bundle_identifier: "#{BASE_BUNDLE_ID}#{bundle_suffix}.Widget",
profile_name: "#{BASE_BUNDLE_ID}#{bundle_suffix}.Widget AppStore",
targets: ["WidgetExtension"]
)
end
# Helper method to build and upload to TestFlight
def build_and_upload(
api_key:,
bundle_id_suffix: "",
configuration: "Release",
distribute_external: true,
version_number: nil
)
bundle_suffix = bundle_id_suffix.empty? ? "" : ".#{bundle_id_suffix}"
app_identifier = "#{BASE_BUNDLE_ID}#{bundle_suffix}"
# Set version number if provided
if version_number
increment_version_number(version_number: version_number)
end
# Increment build number
increment_build_number(
build_number: latest_testflight_build_number(
api_key: api_key,
app_identifier: "app.alextran.immich"
app_identifier: app_identifier
) + 1,
xcodeproj: "./Runner.xcodeproj"
)
@@ -60,35 +99,99 @@ platform :ios do
build_app(
scheme: "Runner",
workspace: "Runner.xcworkspace",
configuration: configuration,
export_method: "app-store",
xcargs: "CODE_SIGN_IDENTITY='#{CODE_SIGN_IDENTITY}' CODE_SIGN_STYLE=Manual",
export_options: {
provisioningProfiles: {
"app.alextran.immich" => "app.alextran.immich AppStore"
}
"#{app_identifier}" => "#{app_identifier} AppStore",
"#{app_identifier}.ShareExtension" => "#{app_identifier}.ShareExtension AppStore",
"#{app_identifier}.Widget" => "#{app_identifier}.Widget AppStore"
},
signingStyle: "manual",
signingCertificate: CODE_SIGN_IDENTITY
}
)
# Upload to TestFlight
upload_to_testflight(
api_key: api_key,
skip_waiting_for_build_processing: true
skip_waiting_for_build_processing: true,
distribute_external: distribute_external
)
end
desc "iOS Development Build to TestFlight (requires separate bundle ID)"
lane :gha_testflight_dev do
api_key = get_api_key
# Install development provisioning profiles
install_provisioning_profile(path: "profile_dev.mobileprovision")
install_provisioning_profile(path: "profile_dev_share.mobileprovision")
install_provisioning_profile(path: "profile_dev_widget.mobileprovision")
# Configure code signing for dev bundle IDs
configure_code_signing(bundle_id_suffix: "development")
# Build and upload
build_and_upload(
api_key: api_key,
bundle_id_suffix: "development",
configuration: "Profile",
distribute_external: false
)
end
desc "iOS Release"
lane :release do
desc "iOS Release to TestFlight"
lane :gha_release_prod do
api_key = get_api_key
# Install provisioning profiles
install_provisioning_profile(path: "profile.mobileprovision")
install_provisioning_profile(path: "profile_share.mobileprovision")
install_provisioning_profile(path: "profile_widget.mobileprovision")
# Configure code signing for production bundle IDs
configure_code_signing
# Build and upload with version number
build_and_upload(
api_key: api_key,
version_number: "2.1.0"
)
end
desc "iOS Manual Release"
lane :release_manual do
enable_automatic_code_signing(
path: "./Runner.xcodeproj",
targets: ["Runner", "ShareExtension", "WidgetExtension"]
)
increment_version_number(
version_number: "2.2.0"
version_number: "2.2.2"
)
increment_build_number(
build_number: latest_testflight_build_number + 1,
)
build_app(scheme: "Runner",
workspace: "Runner.xcworkspace",
xcargs: "-allowProvisioningUpdates")
# Build archive with automatic signing
gym(
scheme: "Runner",
workspace: "Runner.xcworkspace",
configuration: "Release",
export_method: "app-store",
skip_package_ipa: false,
xcargs: "-allowProvisioningUpdates",
export_options: {
method: "app-store",
signingStyle: "automatic",
uploadBitcode: false,
uploadSymbols: true,
compileBitcode: false
}
)
upload_to_testflight(
skip_waiting_for_build_processing: true
)

View File

@@ -15,13 +15,29 @@ For _fastlane_ installation instructions, see [Installing _fastlane_](https://do
## iOS
### ios release
### ios gha_testflight_dev
```sh
[bundle exec] fastlane ios release
[bundle exec] fastlane ios gha_testflight_dev
```
iOS Release
iOS Development Build to TestFlight (requires separate bundle ID)
### ios gha_release_prod
```sh
[bundle exec] fastlane ios gha_release_prod
```
iOS Release to TestFlight
### ios release_manual
```sh
[bundle exec] fastlane ios release_manual
```
iOS Manual Release
----

View File

@@ -53,3 +53,8 @@ const int kMinMonthsToEnableScrubberSnap = 12;
const String kImmichAppStoreLink = "https://apps.apple.com/app/immich/id6449244941";
const String kImmichPlayStoreLink = "https://play.google.com/store/apps/details?id=app.alextran.immich";
const String kImmichLatestRelease = "https://github.com/immich-app/immich/releases/latest";
const int kPhotoTabIndex = 0;
const int kSearchTabIndex = 1;
const int kAlbumTabIndex = 2;
const int kLibraryTabIndex = 3;

View File

@@ -132,7 +132,8 @@ class SyncStreamService {
return;
// SyncCompleteV1 is used to signal the completion of the sync process. Cleanup stale assets and signal completion
case SyncEntityType.syncCompleteV1:
return _syncStreamRepository.pruneAssets();
return;
// return _syncStreamRepository.pruneAssets();
// Request to reset the client state. Clear everything related to remote entities
case SyncEntityType.syncResetV1:
return _syncStreamRepository.reset();

View File

@@ -4,6 +4,7 @@ import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/constants.dart';
import 'package:immich_mobile/domain/models/timeline.model.dart';
import 'package:immich_mobile/domain/utils/event_stream.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
@@ -107,30 +108,30 @@ class _TabShellPageState extends ConsumerState<TabShellPage> {
void _onNavigationSelected(TabsRouter router, int index, WidgetRef ref) {
// On Photos page menu tapped
if (router.activeIndex == 0 && index == 0) {
if (router.activeIndex == kPhotoTabIndex && index == kPhotoTabIndex) {
EventStream.shared.emit(const ScrollToTopEvent());
}
if (index == 0) {
if (index == kPhotoTabIndex) {
ref.invalidate(driftMemoryFutureProvider);
}
if (router.activeIndex != 1 && index == 1) {
if (router.activeIndex != kSearchTabIndex && index == kSearchTabIndex) {
ref.read(searchPreFilterProvider.notifier).clear();
}
// On Search page tapped
if (router.activeIndex == 1 && index == 1) {
if (router.activeIndex == kSearchTabIndex && index == kSearchTabIndex) {
ref.read(searchInputFocusProvider).requestFocus();
}
// Album page
if (index == 2) {
if (index == kAlbumTabIndex) {
ref.read(remoteAlbumProvider.notifier).refresh();
}
// Library page
if (index == 3) {
if (index == kLibraryTabIndex) {
ref.invalidate(localAlbumProvider);
ref.invalidate(driftGetAllPeopleProvider);
}

View File

@@ -73,27 +73,29 @@ class DriftSearchPage extends HookConsumerWidget {
);
}
search() async {
if (filter.value.isEmpty) {
searchFilter(SearchFilter filter) async {
if (filter.isEmpty) {
return;
}
if (preFilter == null && filter.value == previousFilter.value) {
if (preFilter == null && filter == previousFilter.value) {
return;
}
isSearching.value = true;
ref.watch(paginatedSearchProvider.notifier).clear();
final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value);
final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter);
if (!hasResult) {
context.showSnackBar(searchInfoSnackBar('search_no_result'.t(context: context)));
}
previousFilter.value = filter.value;
previousFilter.value = filter;
isSearching.value = false;
}
search() => searchFilter(filter.value);
loadMoreSearchResult() async {
isSearching.value = true;
final hasResult = await ref.watch(paginatedSearchProvider.notifier).search(filter.value);
@@ -108,7 +110,7 @@ class DriftSearchPage extends HookConsumerWidget {
searchPreFilter() {
if (preFilter != null) {
Future.delayed(Duration.zero, () {
search();
searchFilter(preFilter);
if (preFilter.location.city != null) {
locationCurrentFilterWidget.value = Text(preFilter.location.city!, style: context.textTheme.labelLarge);
@@ -122,7 +124,7 @@ class DriftSearchPage extends HookConsumerWidget {
searchPreFilter();
return null;
}, []);
}, [preFilter]);
showPeoplePicker() {
handleOnSelect(Set<PersonDto> value) {

View File

@@ -35,7 +35,8 @@ class SimilarPhotosActionButton extends ConsumerWidget {
mediaType: AssetType.image,
),
);
unawaited(context.router.popAndPush(const DriftSearchRoute()));
unawaited(context.navigateTo(const DriftSearchRoute()));
}
@override

View File

@@ -141,14 +141,28 @@ class _AssetDetailBottomSheet extends ConsumerWidget {
}
Widget _buildAppearsInList(WidgetRef ref, BuildContext context) {
final isRemote = ref.watch(currentAssetNotifier)?.hasRemote ?? false;
if (!isRemote) {
final aseet = ref.watch(currentAssetNotifier);
if (aseet == null) {
return const SizedBox.shrink();
}
if (!aseet.hasRemote) {
return const SizedBox.shrink();
}
String? remoteAssetId;
if (aseet is RemoteAsset) {
remoteAssetId = aseet.id;
} else if (aseet is LocalAsset) {
remoteAssetId = aseet.remoteAssetId;
}
if (remoteAssetId == null) {
return const SizedBox.shrink();
}
final remoteAsset = ref.watch(currentAssetNotifier) as RemoteAsset;
final userId = ref.watch(currentUserProvider)?.id;
final assetAlbums = ref.watch(albumsContainingAssetProvider(remoteAsset.id));
final assetAlbums = ref.watch(albumsContainingAssetProvider(remoteAssetId));
return assetAlbums.when(
data: (albums) {

View File

@@ -228,6 +228,8 @@ class _SliverTimelineState extends ConsumerState<_SliverTimeline> {
curve: Curves.easeInOut,
)
.whenComplete(() => ref.read(timelineStateProvider.notifier).setScrubbing(false));
} else {
ref.read(timelineStateProvider.notifier).setScrubbing(false);
}
});
}

View File

@@ -5,6 +5,7 @@ import 'package:immich_mobile/domain/services/timeline.service.dart';
import 'package:immich_mobile/mixins/error_logger.mixin.dart';
import 'package:immich_mobile/models/activities/activity.model.dart';
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart';
import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart';
import 'package:immich_mobile/repositories/activity_api.repository.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:logging/logging.dart';
@@ -69,6 +70,7 @@ class ActivityService with ErrorLoggerMixin {
return AssetViewerRoute(
initialIndex: 0,
timelineService: _timelineFactory.fromAssets([asset], TimelineOrigin.albumActivities),
currentAlbum: ref.read(currentRemoteAlbumProvider),
);
}

View File

@@ -127,7 +127,7 @@ enum ActionButtonType {
context.currentAlbum!.isShared,
ActionButtonType.similarPhotos =>
!context.isInLockedView && //
context.asset.hasRemote,
context.asset is RemoteAsset,
};
}

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: 2.2.0
- API version: 2.2.2
- Generator version: 7.8.0
- Build package: org.openapitools.codegen.languages.DartClientCodegen

View File

@@ -1233,8 +1233,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: d921ae2
resolved-ref: d921ae210e294d2821954009ec2cc8aeae918725
ref: e132bc3
resolved-ref: e132bc3ecc6a6d8fc2089d96f849c8a13129500e
url: "https://github.com/immich-app/native_video_player"
source: git
version: "1.3.1"

View File

@@ -2,7 +2,7 @@ name: immich_mobile
description: Immich - selfhosted backup media file on mobile phone
publish_to: 'none'
version: 2.2.0+3023
version: 2.2.2+3025
environment:
sdk: '>=3.8.0 <4.0.0'
@@ -57,7 +57,7 @@ dependencies:
native_video_player:
git:
url: https://github.com/immich-app/native_video_player
ref: 'd921ae2'
ref: 'e132bc3'
network_info_plus: ^6.1.3
octo_image: ^2.1.0
openapi:

View File

@@ -10006,7 +10006,7 @@
"info": {
"title": "Immich",
"description": "Immich API",
"version": "2.2.0",
"version": "2.2.2",
"contact": {}
},
"tags": [],

View File

@@ -1,6 +1,6 @@
{
"name": "@immich/sdk",
"version": "2.2.0",
"version": "2.2.2",
"description": "Auto-generated TypeScript SDK for the Immich API",
"type": "module",
"main": "./build/index.js",

View File

@@ -1,6 +1,6 @@
/**
* Immich
* 2.2.0
* 2.2.2
* DO NOT MODIFY - This file has been generated using oazapfts.
* See https://www.npmjs.com/package/oazapfts
*/

View File

@@ -1,6 +1,6 @@
{
"name": "immich",
"version": "2.2.0",
"version": "2.2.2",
"description": "",
"author": "",
"private": true,

View File

@@ -57,6 +57,28 @@ describe(AssetController.name, () => {
});
});
describe('PUT /assets/copy', () => {
it('should be an authenticated route', async () => {
await request(ctx.getHttpServer()).get(`/assets/copy`);
expect(ctx.authenticate).toHaveBeenCalled();
});
it('should require target and source id', async () => {
const { status, body } = await request(ctx.getHttpServer()).put('/assets/copy').send({});
expect(status).toBe(400);
expect(body).toEqual(
factory.responses.badRequest(expect.arrayContaining(['sourceId must be a UUID', 'targetId must be a UUID'])),
);
});
it('should work', async () => {
const { status } = await request(ctx.getHttpServer())
.put('/assets/copy')
.send({ sourceId: factory.uuid(), targetId: factory.uuid() });
expect(status).toBe(204);
});
});
describe('PUT /assets/:id', () => {
it('should be an authenticated route', async () => {
await request(ctx.getHttpServer()).get(`/assets/123`);

View File

@@ -81,6 +81,13 @@ export class AssetController {
return this.service.get(auth, id) as Promise<AssetResponseDto>;
}
@Put('copy')
@Authenticated({ permission: Permission.AssetCopy })
@HttpCode(HttpStatus.NO_CONTENT)
copyAsset(@Auth() auth: AuthDto, @Body() dto: AssetCopyDto): Promise<void> {
return this.service.copy(auth, dto);
}
@Put(':id')
@Authenticated({ permission: Permission.AssetUpdate })
updateAsset(
@@ -91,13 +98,6 @@ export class AssetController {
return this.service.update(auth, id, dto);
}
@Put('copy')
@Authenticated({ permission: Permission.AssetCopy })
@HttpCode(HttpStatus.NO_CONTENT)
copyAsset(@Auth() auth: AuthDto, @Body() dto: AssetCopyDto): Promise<void> {
return this.service.copy(auth, dto);
}
@Get(':id/metadata')
@Authenticated({ permission: Permission.AssetRead })
getAssetMetadata(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<AssetMetadataResponseDto[]> {

View File

@@ -217,7 +217,7 @@ export class AssetService extends BaseService {
}
if (stack) {
await this.copyStack(sourceAsset, targetAsset);
await this.copyStack({ sourceAsset, targetAsset });
}
if (favorite) {
@@ -225,14 +225,17 @@ export class AssetService extends BaseService {
}
if (sidecar) {
await this.copySidecar(sourceAsset, targetAsset);
await this.copySidecar({ sourceAsset, targetAsset });
}
}
private async copyStack(
sourceAsset: { id: string; stackId: string | null },
targetAsset: { id: string; stackId: string | null },
) {
private async copyStack({
sourceAsset,
targetAsset,
}: {
sourceAsset: { id: string; stackId: string | null };
targetAsset: { id: string; stackId: string | null };
}) {
if (!sourceAsset.stackId) {
return;
}
@@ -245,21 +248,24 @@ export class AssetService extends BaseService {
}
}
private async copySidecar(
targetAsset: { sidecarPath: string | null },
sourceAsset: { id: string; sidecarPath: string | null; originalPath: string },
) {
if (!targetAsset.sidecarPath) {
private async copySidecar({
sourceAsset,
targetAsset,
}: {
sourceAsset: { sidecarPath: string | null };
targetAsset: { id: string; sidecarPath: string | null; originalPath: string };
}) {
if (!sourceAsset.sidecarPath) {
return;
}
if (sourceAsset.sidecarPath) {
await this.storageRepository.unlink(sourceAsset.sidecarPath);
if (targetAsset.sidecarPath) {
await this.storageRepository.unlink(targetAsset.sidecarPath);
}
await this.storageRepository.copyFile(targetAsset.sidecarPath, `${sourceAsset.originalPath}.xmp`);
await this.assetRepository.update({ id: sourceAsset.id, sidecarPath: `${sourceAsset.originalPath}.xmp` });
await this.jobRepository.queue({ name: JobName.AssetExtractMetadata, data: { id: sourceAsset.id } });
await this.storageRepository.copyFile(sourceAsset.sidecarPath, `${targetAsset.originalPath}.xmp`);
await this.assetRepository.update({ id: targetAsset.id, sidecarPath: `${targetAsset.originalPath}.xmp` });
await this.jobRepository.queue({ name: JobName.AssetExtractMetadata, data: { id: targetAsset.id } });
}
@OnJob({ name: JobName.AssetDeleteCheck, queue: QueueName.BackgroundTask })

View File

@@ -1,6 +1,6 @@
{
"name": "immich-web",
"version": "2.2.0",
"version": "2.2.2",
"license": "GNU Affero General Public License version 3",
"type": "module",
"scripts": {

View File

@@ -0,0 +1,78 @@
<script lang="ts">
import ApiKeyGrid from '$lib/components/user-settings-page/user-api-key-grid.svelte';
import { Permission } from '@immich/sdk';
import { Checkbox, IconButton, Input, Label } from '@immich/ui';
import { mdiClose } from '@mdi/js';
import { t } from 'svelte-i18n';
type Props = {
selectedPermissions: Permission[];
};
let { selectedPermissions = $bindable([]) }: Props = $props();
const permissions: Record<string, Permission[]> = {};
for (const permission of Object.values(Permission)) {
if (permission === Permission.All) {
continue;
}
const [group] = permission.split('.');
if (!permissions[group]) {
permissions[group] = [];
}
permissions[group].push(permission);
}
let searchValue = $state('');
let allItemsSelected = $derived(selectedPermissions.length === Object.keys(Permission).length - 1);
const matchFilter = (search: string) => {
search = search.toLowerCase();
return ([title, items]: [string, Permission[]]) =>
title.toLowerCase().includes(search) || items.some((item) => item.toLowerCase().includes(search));
};
const onCheckedAllChange = (checked: boolean) => {
selectedPermissions = checked
? Object.values(Permission).filter((permission) => permission !== Permission.All)
: [];
};
const filteredResults = $derived(Object.entries(permissions).filter(matchFilter(searchValue)));
const handleSelectItems = (items: Permission[]) =>
(selectedPermissions = Array.from(new Set([...selectedPermissions, ...items])));
const handleDeselectItems = (items: Permission[]) =>
(selectedPermissions = selectedPermissions.filter((item) => !items.includes(item)));
</script>
<Label label={$t('permission')} for="permission-container" />
<div class="flex items-center gap-2 m-4" id="permission-container">
<Checkbox id="input-select-all" size="tiny" checked={allItemsSelected} onCheckedChange={onCheckedAllChange} />
<Label label={$t('select_all')} for="input-select-all" />
</div>
<div class="ms-4 flex flex-col gap-2">
<Input bind:value={searchValue} placeholder={$t('search')}>
{#snippet trailingIcon()}
{#if searchValue}
<IconButton
icon={mdiClose}
size="small"
variant="ghost"
shape="round"
color="secondary"
class="me-1"
onclick={() => (searchValue = '')}
aria-label={$t('clear')}
/>
{/if}
{/snippet}
</Input>
{#each filteredResults as [title, subItems] (title)}
<ApiKeyGrid {title} {subItems} selectedItems={selectedPermissions} {handleSelectItems} {handleDeselectItems} />
{/each}
</div>

View File

@@ -25,7 +25,7 @@
<p>{$t('admin.storage_template_date_time_description')}</p>
<p>{$t('admin.storage_template_date_time_sample', { values: { date: '2022-02-03T20:03:05.250' } })}</p>
</div>
<div class="flex gap-[40px]">
<div class="flex gap-10">
<div>
<p class="uppercase font-medium text-primary">{$t('year')}</p>
<ul>

View File

@@ -7,7 +7,7 @@
</div>
<div class="p-4 mt-2 text-xs bg-gray-200 rounded-lg dark:bg-gray-700 dark:text-immich-dark-fg">
<div class="flex gap-[50px]">
<div class="flex gap-12">
<div>
<p class="uppercase font-medium text-primary">{$t('filename')}</p>
<ul>

View File

@@ -32,7 +32,7 @@
</script>
<tr
class="flex h-[50px] w-full place-items-center border-[3px] border-transparent p-2 text-center even:bg-subtle/20 odd:bg-subtle/80 hover:cursor-pointer hover:border-immich-primary/75 odd:dark:bg-immich-dark-gray/75 even:dark:bg-immich-dark-gray/50 dark:hover:border-immich-dark-primary/75 md:p-5"
class="flex h-12 w-full place-items-center border-3 border-transparent p-2 text-center even:bg-subtle/20 odd:bg-subtle/80 hover:cursor-pointer hover:border-immich-primary/75 odd:dark:bg-immich-dark-gray/75 even:dark:bg-immich-dark-gray/50 dark:hover:border-immich-dark-primary/75 md:p-5"
onclick={() => goto(resolve(`${AppRoute.ALBUMS}/${album.id}`))}
{oncontextmenu}
>

View File

@@ -141,14 +141,14 @@
<UserAvatar user={reaction.user} size="sm" />
</div>
<div class="w-full leading-4 overflow-hidden self-center break-words text-sm">{reaction.comment}</div>
<div class="w-full leading-4 overflow-hidden self-center wrap-break-word text-sm">{reaction.comment}</div>
{#if assetId === undefined && reaction.assetId}
<a
class="aspect-square w-[75px] h-[75px]"
class="aspect-square w-19 h-19"
href={resolve(`${AppRoute.ALBUMS}/${albumId}/photos/${reaction.assetId}`)}
>
<img
class="rounded-lg w-[75px] h-[75px] object-cover"
class="rounded-lg w-19 h-19 object-cover"
src={getAssetThumbnailUrl(reaction.assetId)}
alt="Profile picture of {reaction.user.name}, who commented on this asset"
/>
@@ -197,11 +197,11 @@
</div>
{#if assetId === undefined && reaction.assetId}
<a
class="aspect-square w-[75px] h-[75px]"
class="aspect-square w-19 h-19"
href={resolve(`${AppRoute.ALBUMS}/${albumId}/photos/${reaction.assetId}`)}
>
<img
class="rounded-lg w-[75px] h-[75px] object-cover"
class="rounded-lg w-19 h-19 object-cover"
src={getAssetThumbnailUrl(reaction.assetId)}
alt="Profile picture of {reaction.user.name}, who liked this asset"
/>

View File

@@ -593,7 +593,7 @@
{#if stackedAsset.id === asset.id}
<div class="w-full flex place-items-center place-content-center">
<div class="w-2 h-2 bg-white rounded-full flex mt-[2px]"></div>
<div class="w-2 h-2 bg-white rounded-full flex mt-0.5"></div>
</div>
{/if}
</div>

View File

@@ -31,13 +31,13 @@
<section class="px-4 mt-10">
<AutogrowTextarea
content={description}
class="max-h-[500px] w-full border-b border-gray-500 bg-transparent text-base text-black outline-none transition-all focus:border-b-2 focus:border-immich-primary disabled:border-none dark:text-white dark:focus:border-immich-dark-primary immich-scrollbar"
class="max-h-125 w-full border-b border-gray-500 bg-transparent text-base text-black outline-none transition-all focus:border-b-2 focus:border-immich-primary disabled:border-none dark:text-white dark:focus:border-immich-dark-primary immich-scrollbar"
onContentUpdate={handleFocusOut}
placeholder={$t('add_a_description')}
/>
</section>
{:else if description}
<section class="px-4 mt-6">
<p class="break-words whitespace-pre-line w-full text-black dark:text-white text-base">{description}</p>
<p class="wrap-break-word whitespace-pre-line w-full text-black dark:text-white text-base">{description}</p>
</section>
{/if}

View File

@@ -199,7 +199,7 @@
{#each people as person, index (person.id)}
{#if showingHiddenPeople || !person.isHidden}
<a
class="w-[90px]"
class="w-22"
href={resolve(
`${AppRoute.PEOPLE}/${person.id}?${QueryParameter.PREVIOUS_ROUTE}=${
currentAlbum?.id ? `${AppRoute.ALBUMS}/${currentAlbum?.id}` : AppRoute.PHOTOS
@@ -439,7 +439,7 @@
</section>
{#if latlng && $featureFlags.loaded && $featureFlags.map}
<div class="h-[360px]">
<div class="h-90">
{#await import('$lib/components/shared-components/map/map.svelte')}
{#await delay(timeToLoadTheMap) then}
<!-- show the loading spinner only if loading the map takes too much time -->
@@ -511,7 +511,7 @@
<div>
<img
alt={album.albumName}
class="h-[50px] w-[50px] rounded object-cover"
class="h-12.5 w-12.5 rounded object-cover"
src={album.albumThumbnailAssetId &&
getAssetThumbnailUrl({ id: album.albumThumbnailAssetId, size: AssetMediaSize.Preview })}
draggable="false"

View File

@@ -16,10 +16,10 @@
{#if downloadManager.isDownloading}
<div
transition:fly={{ x: -100, duration: 350 }}
class="fixed bottom-10 start-2 max-h-[270px] w-[315px] rounded-2xl border dark:border-white/10 p-4 shadow-lg bg-subtle"
class="fixed bottom-10 start-2 max-h-67.5 w-79 rounded-2xl border dark:border-white/10 p-4 shadow-lg bg-subtle"
>
<Heading size="tiny">{$t('downloading')}</Heading>
<div class="my-2 mb-2 flex max-h-[200px] flex-col overflow-y-auto text-sm">
<div class="my-2 mb-2 flex max-h-50 flex-col overflow-y-auto text-sm">
{#each Object.keys(downloadManager.assets) as downloadKey (downloadKey)}
{@const download = downloadManager.assets[downloadKey]}
<div class="mb-2 flex place-items-center" transition:slide>
@@ -31,10 +31,10 @@
{/if}
</div>
<div class="flex place-items-center gap-2">
<div class="h-[10px] w-full rounded-full bg-neutral-200 dark:bg-neutral-600">
<div class="h-[10px] rounded-full bg-primary" style={`width: ${download.percentage}%`}></div>
<div class="h-2.5 w-full rounded-full bg-neutral-200 dark:bg-neutral-600">
<div class="h-2.5 rounded-full bg-primary" style={`width: ${download.percentage}%`}></div>
</div>
<p class="min-w-[4em] whitespace-nowrap text-right">
<p class="min-w-16 whitespace-nowrap text-right">
<span class="text-primary">
{(download.percentage / 100).toLocaleString($locale, { style: 'percent' })}
</span>

View File

@@ -322,7 +322,7 @@
<Input placeholder={$t('search_people')} bind:value={searchTerm} size="tiny" />
</div>
<div class="h-[250px] overflow-y-auto mt-2">
<div class="h-62.5 overflow-y-auto mt-2">
{#if filteredCandidates.length > 0}
<div class="mt-2 rounded-lg">
{#each filteredCandidates as person (person.id)}

View File

@@ -257,7 +257,7 @@
<!-- eslint-disable-next-line svelte/require-each-key -->
{#each getBoundingBox($boundingBoxesArray, $photoZoomState, $photoViewerImgElement) as boundingbox}
<div
class="absolute border-solid border-white border-[3px] rounded-lg"
class="absolute border-solid border-white border-3 rounded-lg"
style="top: {boundingbox.top}px; left: {boundingbox.left}px; height: {boundingbox.height}px; width: {boundingbox.width}px;"
></div>
{/each}

View File

@@ -68,7 +68,7 @@
circle && 'rounded-full',
shadow && 'shadow-lg',
(circle || !heightStyle) && 'aspect-square',
border && 'border-[3px] border-immich-dark-primary/80 hover:border-immich-primary',
border && 'border-3 border-immich-dark-primary/80 hover:border-immich-primary',
brokenAssetClass,
]
.filter(Boolean)

View File

@@ -232,7 +232,7 @@
></div>
<div
class={['group absolute -top-[0px] -bottom-[0px]', { 'cursor-not-allowed': disabled, 'cursor-pointer': !disabled }]}
class={['group absolute top-0 bottom-0', { 'cursor-not-allowed': disabled, 'cursor-pointer': !disabled }]}
style:width="inherit"
style:height="inherit"
>

View File

@@ -73,7 +73,7 @@
<section
transition:fly={{ x: 360, duration: 100, easing: linear }}
class="absolute top-0 h-full w-[360px] overflow-x-hidden p-2 dark:text-immich-dark-fg bg-light"
class="absolute top-0 h-full w-90 overflow-x-hidden p-2 dark:text-immich-dark-fg bg-light"
>
<div class="flex place-items-center justify-between gap-2">
{#if !searchFaces}
@@ -157,7 +157,7 @@
{#each showPeople as person (person.id)}
{#if !editedFace.person || person.id !== editedFace.person.id}
<div class="w-fit">
<button type="button" class="w-[90px]" onclick={() => onReassign(person)}>
<button type="button" class="w-22.5" onclick={() => onReassign(person)}>
<div class="relative">
<ImageThumbnail
curve

View File

@@ -103,9 +103,9 @@
</Button>
{/snippet}
</ControlAppBar>
<section class="px-[70px] pt-[100px]">
<section class="px-17.5 pt-25">
<section id="merge-face-selector">
<div class="mb-10 h-[200px] place-content-center place-items-center">
<div class="mb-10 h-50 place-content-center place-items-center">
<p class="mb-4 text-center uppercase dark:text-white">{$t('choose_matching_people_to_merge')}</p>
<div class="grid grid-flow-col-dense place-content-center place-items-center gap-4">

View File

@@ -186,7 +186,7 @@
<section
transition:fly={{ x: 360, duration: 100, easing: linear }}
class="absolute top-0 h-full w-[360px] overflow-x-hidden p-2 dark:text-immich-dark-fg bg-light"
class="absolute top-0 h-full w-90 overflow-x-hidden p-2 dark:text-immich-dark-fg bg-light"
>
<div class="flex place-items-center justify-between gap-2">
<div class="flex items-center gap-2">
@@ -222,11 +222,11 @@
{:else}
{#each peopleWithFaces as face, index (face.id)}
{@const personName = face.person ? face.person?.name : $t('face_unassigned')}
<div class="relative h-[115px] w-[95px]">
<div class="relative h-29 w-24">
<div
role="button"
tabindex={index}
class="absolute start-0 top-0 h-[90px] w-[90px] cursor-default"
class="absolute start-0 top-0 h-22.5 w-22.5 cursor-default"
onfocus={() => ($boundingBoxesArray = [peopleWithFaces[index]])}
onmouseover={() => ($boundingBoxesArray = [peopleWithFaces[index]])}
onmouseleave={() => ($boundingBoxesArray = [])}
@@ -302,7 +302,7 @@
</p>
{/if}
<div class="absolute -end-[3px] -top-[3px] h-[20px] w-[20px] rounded-full">
<div class="absolute -end-[3px] -top-[3px] h-5 w-5 rounded-full">
{#if selectedPersonToCreate[face.id] || selectedPersonToReassign[face.id]}
<IconButton
shape="round"
@@ -326,7 +326,7 @@
/>
{/if}
</div>
<div class="absolute end-[33px] -top-[3px] h-[20px] w-[20px] rounded-full">
<div class="absolute end-8 -top-[3px] h-5 w-5 rounded-full">
{#if !selectedPersonToCreate[face.id] && !selectedPersonToReassign[face.id] && !face.person}
<div
class="flex place-content-center place-items-center rounded-full bg-[#d3d3d3] p-1 transition-all absolute start-1/2 top-1/2 translate-x-[-50%] translate-y-[-50%] transform"
@@ -336,7 +336,7 @@
{/if}
</div>
{#if face.person != null}
<div class="absolute -end-[3px] top-[33px] h-[20px] w-[20px] rounded-full">
<div class="absolute -end-[3px] top-8 h-5 w-5 rounded-full">
<IconButton
shape="round"
color="danger"

View File

@@ -147,10 +147,10 @@
{/snippet}
</ControlAppBar>
{@render merge?.()}
<section class="px-[70px] pt-[100px]">
<section class="px-17.5 pt-25">
<section id="merge-face-selector relative">
{#if selectedPerson !== null}
<div class="mb-10 h-[200px] place-content-center place-items-center">
<div class="mb-10 h-50 place-content-center place-items-center">
<p class="mb-4 text-center uppercase dark:text-white">Choose matching faces to re assign</p>
<div class="grid grid-flow-col-dense place-content-center place-items-center gap-4">

View File

@@ -156,7 +156,7 @@
<tbody class="block w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray">
{#each validatedPaths as validatedPath, listIndex (validatedPath.importPath)}
<tr
class="flex h-[80px] w-full place-items-center text-center dark:text-immich-dark-fg even:bg-subtle/20 odd:bg-subtle/80"
class="flex h-20 w-full place-items-center text-center dark:text-immich-dark-fg even:bg-subtle/20 odd:bg-subtle/80"
>
<td class="w-1/8 text-ellipsis ps-8 text-sm">
{#if validatedPath.isValid}
@@ -180,7 +180,7 @@
</tr>
{/each}
<tr
class="flex h-[80px] w-full place-items-center text-center dark:text-immich-dark-fg even:bg-subtle/20 odd:bg-subtle/80"
class="flex h-20 w-full place-items-center text-center dark:text-immich-dark-fg even:bg-subtle/20 odd:bg-subtle/80"
>
<td class="w-4/5 text-ellipsis px-4 text-sm">
{#if importPaths.length === 0}

View File

@@ -111,7 +111,7 @@
<tbody class="block w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray">
{#each exclusionPatterns as exclusionPattern, listIndex (exclusionPattern)}
<tr
class="flex h-[80px] w-full place-items-center text-center dark:text-immich-dark-fg even:bg-subtle/20 odd:bg-subtle/80"
class="flex h-20 w-full place-items-center text-center dark:text-immich-dark-fg even:bg-subtle/20 odd:bg-subtle/80"
>
<td class="w-3/4 text-ellipsis px-4 text-sm">{exclusionPattern}</td>
<td class="w-1/4 text-ellipsis flex justify-center">
@@ -128,7 +128,7 @@
</tr>
{/each}
<tr
class="flex h-[80px] w-full place-items-center text-center dark:text-immich-dark-fg even:bg-subtle/20 odd:bg-subtle/80"
class="flex h-20 w-full place-items-center text-center dark:text-immich-dark-fg even:bg-subtle/20 odd:bg-subtle/80"
>
<td class="w-3/4 text-ellipsis px-4 text-sm">
{#if exclusionPatterns.length === 0}

View File

@@ -53,9 +53,7 @@
const commonClasses = 'flex place-items-center justify-between w-full py-2 sm:py-4 pe-4 ps-6';
</script>
<div
class="flex flex-col overflow-hidden rounded-2xl bg-gray-100 dark:bg-immich-dark-gray sm:flex-row sm:rounded-[35px]"
>
<div class="flex flex-col overflow-hidden rounded-2xl bg-gray-100 dark:bg-immich-dark-gray sm:flex-row sm:rounded-9">
<div class="flex w-full flex-col">
{#if queueStatus.isPaused}
<JobTileStatus color="warning">{$t('paused')}</JobTileStatus>

View File

@@ -24,7 +24,7 @@
<section>
<div class="flex place-items-center border-b px-6 py-4 dark:border-b-immich-dark-gray">
<a class="flex place-items-center gap-2 hover:cursor-pointer" href="/photos">
<ImmichLogo class="h-[50px]" />
<ImmichLogo class="h-12.5" />
</a>
</div>
</section>
@@ -32,7 +32,7 @@
<div class="fixed top-0 flex h-full w-full place-content-center place-items-center overflow-hidden bg-black/50">
<div>
<div
class="w-[500px] max-w-[95vw] rounded-3xl border shadow-sm dark:border-immich-dark-gray dark:text-immich-dark-fg bg-subtle/80"
class="w-125 max-w-[95vw] rounded-3xl border shadow-sm dark:border-immich-dark-gray dark:text-immich-dark-fg bg-subtle/80"
>
<div>
<div class="flex items-center justify-between gap-4 px-4 py-4">
@@ -52,7 +52,7 @@
<hr />
<div class="immich-scrollbar max-h-[75vh] min-h-[300px] gap-4 overflow-y-auto p-4 pb-4">
<div class="immich-scrollbar max-h-[75vh] min-h-75 gap-4 overflow-y-auto p-4 pb-4">
<div class="flex w-full flex-col gap-2">
<p class="text-red-500">{error?.message} ({error?.code})</p>
{#if error?.stack}

View File

@@ -372,7 +372,7 @@
{/snippet}
<div class="flex place-content-center place-items-center gap-2 overflow-hidden">
<div class="w-[50px] dark">
<div class="w-12.5 dark">
<IconButton
shape="round"
variant="ghost"
@@ -385,8 +385,8 @@
{#each current.memory.assets as asset, index (asset.id)}
<a class="relative w-full py-2" href={asHref(asset)} aria-label={$t('view')}>
<span class="absolute start-0 h-[2px] w-full bg-gray-500"></span>
<span class="absolute start-0 h-[2px] bg-white" style:width={`${toProgressPercentage(index)}%`}></span>
<span class="absolute start-0 h-0.5 w-full bg-gray-500"></span>
<span class="absolute start-0 h-0.5 bg-white" style:width={`${toProgressPercentage(index)}%`}></span>
</a>
{/each}
@@ -397,7 +397,7 @@
</div>
{#if currentTimelineAssets.some(({ isVideo }) => isVideo)}
<div class="w-[50px] dark">
<div class="w-12.5 dark">
<IconButton
shape="round"
variant="ghost"
@@ -502,7 +502,7 @@
color="secondary"
aria-label={isSaved ? $t('unfavorite') : $t('favorite')}
onclick={() => handleSaveMemory()}
class="w-[48px] h-[48px]"
class="w-12 h-12"
/>
<!-- <IconButton
icon={mdiShareVariantOutline}

View File

@@ -9,7 +9,7 @@
</script>
<div class="gap-4">
<ImmichLogo noText class="h-[100px] mb-2" />
<ImmichLogo noText class="h-25 mb-2" />
<p class="font-medium mb-6 text-6xl text-primary">
{$t('onboarding_welcome_user', { values: { user: $user.name } })}
</p>

View File

@@ -47,10 +47,7 @@
{#if canScrollLeft || canScrollRight}
<div class="sticky start-0 z-1">
{#if canScrollLeft}
<div
class="absolute start-4 max-md:top-[75px] top-[108px] -translate-y-1/2"
transition:fade={{ duration: 200 }}
>
<div class="absolute start-4 max-md:top-19 top-27 -translate-y-1/2" transition:fade={{ duration: 200 }}>
<button
type="button"
class="rounded-full border border-gray-500 bg-gray-100 p-2 text-gray-500 opacity-50 hover:opacity-100"
@@ -63,10 +60,7 @@
</div>
{/if}
{#if canScrollRight}
<div
class="absolute end-4 max-md:top-[75px] top-[108px] -translate-y-1/2 z-1"
transition:fade={{ duration: 200 }}
>
<div class="absolute end-4 max-md:top-19 top-27 -translate-y-1/2 z-1" transition:fade={{ duration: 200 }}>
<button
type="button"
class="rounded-full border border-gray-500 bg-gray-100 p-2 text-gray-500 opacity-50 hover:opacity-100"
@@ -83,7 +77,7 @@
<div class="inline-block" use:resizeObserver={({ width }) => (innerWidth = width)}>
{#each memoryStore.memories as memory (memory.id)}
<a
class="memory-card relative me-2 md:me-4 last:me-0 inline-block aspect-3/4 md:aspect-4/3 max-md:h-[150px] xl:aspect-video h-[216px] rounded-xl"
class="memory-card relative me-2 md:me-4 last:me-0 inline-block aspect-3/4 md:aspect-4/3 max-md:h-37.5 xl:aspect-video h-54 rounded-xl"
href="{AppRoute.MEMORY}?{QueryParameter.ID}={memory.assets[0].id}"
>
<img

View File

@@ -43,12 +43,12 @@
{@const city = item.exifInfo?.city}
<a class="relative" href="{AppRoute.SEARCH}?{getMetadataSearchQuery({ city })}" draggable="false">
<div
class="flex w-[calc((100vw-(72px+5rem))/2)] max-w-[156px] justify-center overflow-hidden rounded-xl brightness-75 filter"
class="flex w-[calc((100vw-(72px+5rem))/2)] max-w-39 justify-center overflow-hidden rounded-xl brightness-75 filter"
>
<img
src={getAssetThumbnailUrl({ id: item.id, size: AssetMediaSize.Thumbnail })}
alt={city}
class="object-cover w-[156px] h-[156px]"
class="object-cover w-39 h-39"
/>
</div>
<span

View File

@@ -20,7 +20,7 @@
});
</script>
<div class="flex h-[140px] w-full flex-col justify-between rounded-3xl bg-subtle text-primary p-5">
<div class="flex h-35 w-full flex-col justify-between rounded-3xl bg-subtle text-primary p-5">
<div class="flex place-items-center gap-4">
<Icon {icon} size="40" />
<Text size="large" fontWeight="bold" class="uppercase">{title}</Text>

View File

@@ -100,10 +100,10 @@
</tr>
</thead>
<tbody
class="block max-h-[320px] w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray dark:text-immich-dark-fg"
class="block max-h-80 w-full overflow-y-auto rounded-md border dark:border-immich-dark-gray dark:text-immich-dark-fg"
>
{#each stats.usageByUser as user (user.userId)}
<tr class="flex h-[50px] w-full place-items-center text-center even:bg-subtle/20 odd:bg-subtle/80">
<tr class="flex h-12.5 w-full place-items-center text-center even:bg-subtle/20 odd:bg-subtle/80">
<td class="w-1/4 text-ellipsis px-2 text-sm">{user.userName}</td>
<td class="w-1/4 text-ellipsis px-2 text-sm"
>{user.photos.toLocaleString($locale)} ({getByteUnitString(user.usagePhotos, $locale, 0)})</td

View File

@@ -136,7 +136,7 @@
{/snippet}
</ControlAppBar>
{/if}
<section class="my-[160px] mx-4" bind:clientHeight={viewport.height} bind:clientWidth={viewport.width}>
<section class="my-40 mx-4" bind:clientHeight={viewport.height} bind:clientWidth={viewport.width}>
<GalleryViewer {assets} {assetInteraction} {viewport} />
</section>
{:else if assets.length === 1}

View File

@@ -172,7 +172,7 @@
</div>
<span>{$t('pick_a_location')}</span>
<div class="h-[500px] min-h-[300px] w-full z-0">
<div class="h-125 min-h-75 w-full z-0">
{#await import('$lib/components/shared-components/map/map.svelte')}
{#await delay(timeToLoadTheMap) then}
<!-- show the loading spinner only if loading the map takes too much time -->

View File

@@ -61,7 +61,7 @@
<div
bind:clientHeight={height}
class="fixed min-w-[200px] w-max max-w-[300px] overflow-hidden rounded-lg shadow-lg z-1"
class="fixed min-w-50 w-max max-w-75 overflow-hidden rounded-lg shadow-lg z-1"
style:left="{left}px"
style:top="{top}px"
transition:slide={{ duration: 250, easing: quintOut }}

View File

@@ -4,5 +4,5 @@
</script>
<a data-sveltekit-preload-data="hover" class="ms-4" href="/">
<ImmichLogo class="h-[50px] max-w-none md:w-auto md:max-w-full" noText={mobileDevice.maxMd} />
<ImmichLogo class="h-12.5 max-w-none md:w-auto md:max-w-full" noText={mobileDevice.maxMd} />
</a>

View File

@@ -359,7 +359,7 @@
>
{#snippet children({ feature })}
<div
class="rounded-full w-[40px] h-[40px] bg-immich-primary text-white flex justify-center items-center font-mono font-bold shadow-lg hover:bg-immich-dark-primary transition-all duration-200 hover:text-immich-dark-bg opacity-90"
class="rounded-full w-10 h-10 bg-immich-primary text-white flex justify-center items-center font-mono font-bold shadow-lg hover:bg-immich-dark-primary transition-all duration-200 hover:text-immich-dark-bg opacity-90"
>
{feature.properties?.point_count?.toLocaleString()}
</div>
@@ -380,7 +380,7 @@
{:else}
<img
src={getAssetThumbnailUrl(feature.properties?.id)}
class="rounded-full w-[60px] h-[60px] border-2 border-immich-primary shadow-lg hover:border-immich-dark-primary transition-all duration-200 hover:scale-150 object-cover bg-immich-primary"
class="rounded-full w-15 h-15 border-2 border-immich-primary shadow-lg hover:border-immich-dark-primary transition-all duration-200 hover:scale-150 object-cover bg-immich-primary"
alt={feature.properties?.city && feature.properties.country
? $t('map_marker_for_images', {
values: { city: feature.properties.city, country: feature.properties.country },

View File

@@ -32,7 +32,7 @@
in:fade={{ duration: 100 }}
out:fade={{ duration: 100 }}
id="account-info-panel"
class="absolute z-1 end-[25px] top-[75px] w-[min(360px,100vw-50px)] rounded-3xl bg-gray-200 shadow-lg dark:border dark:border-immich-dark-gray dark:bg-immich-dark-gray"
class="absolute z-1 end-6 top-19 w-[min(360px,100vw-50px)] rounded-3xl bg-gray-200 shadow-lg dark:border dark:border-immich-dark-gray dark:bg-immich-dark-gray"
use:focusTrap
>
<div

View File

@@ -78,7 +78,7 @@
class="sidebar:hidden"
/>
<a data-sveltekit-preload-data="hover" href={AppRoute.PHOTOS}>
<ImmichLogo class="max-md:h-[48px] h-[50px]" noText={!mobileDevice.isFullSidebar} />
<ImmichLogo class="max-md:h-12 h-12.5" noText={!mobileDevice.isFullSidebar} />
</a>
</div>
<div class="flex justify-between gap-4 lg:gap-8 pe-6">

View File

@@ -100,7 +100,7 @@
</script>
<button
class="min-h-[80px] p-2 py-3 hover:bg-immich-primary/10 dark:hover:bg-immich-dark-primary/10 border-b border-gray-200 dark:border-immich-dark-gray w-full"
class="min-h-20 p-2 py-3 hover:bg-immich-primary/10 dark:hover:bg-immich-dark-primary/10 border-b border-gray-200 dark:border-immich-dark-gray w-full"
type="button"
onclick={() => onclick(notification)}
title={notification.createdAt}

View File

@@ -66,10 +66,10 @@
in:fade={{ duration: 100 }}
out:fade={{ duration: 100 }}
id="notification-panel"
class="absolute right-[25px] top-[70px] z-1 w-[min(360px,100vw-50px)] rounded-3xl bg-gray-100 border border-gray-200 shadow-lg dark:border dark:border-light dark:bg-immich-dark-gray text-light"
class="absolute right-6 top-17.5 z-1 w-[min(360px,100vw-50px)] rounded-3xl bg-gray-100 border border-gray-200 shadow-lg dark:border dark:border-light dark:bg-immich-dark-gray text-light"
use:focusTrap
>
<Stack class="max-h-[500px]">
<Stack class="max-h-125">
<div class="flex justify-between items-center mt-4 mx-4">
<Text size="medium" color="secondary" class="font-semibold">{$t('notifications')}</Text>
<div>

View File

@@ -20,7 +20,7 @@
<p>{$t('purchase_per_user')}</p>
</div>
<div class="flex flex-col justify-between h-[200px] dark:text-immich-gray">
<div class="flex flex-col justify-between h-50 dark:text-immich-gray">
<div class="mt-6 flex flex-col gap-1">
<div class="grid grid-cols-[36px_auto]">
<Icon icon={mdiCheckCircleOutline} size="24" class="text-green-500 self-center" />

View File

@@ -20,7 +20,7 @@
<p>{$t('purchase_per_server')}</p>
</div>
<div class="flex flex-col justify-between h-[200px] dark:text-immich-gray">
<div class="flex flex-col justify-between h-50 dark:text-immich-gray">
<div class="mt-6 flex flex-col gap-1">
<div class="grid grid-cols-[36px_auto]">
<Icon icon={mdiCheckCircleOutline} size="24" class="text-green-500 self-center" />

View File

@@ -235,7 +235,7 @@
name="q"
id="main-search-bar"
class="w-full transition-all border-2 ps-14 py-4 max-md:py-2 text-immich-fg/75 dark:text-immich-dark-fg
{showClearIcon ? 'pe-[90px]' : 'pe-14'}
{showClearIcon ? 'pe-22.5' : 'pe-14'}
{grayTheme ? 'dark:bg-immich-dark-gray' : 'dark:bg-immich-dark-bg'}
{showSuggestions && isSearchSuggestions ? 'rounded-t-3xl' : 'rounded-3xl bg-gray-200'}
{searchStore.isSearchEnabled ? 'border-gray-200 dark:border-gray-700 bg-white' : 'border-transparent'}"

View File

@@ -52,7 +52,7 @@
</script>
{#await peoplePromise}
<div id="spinner" class="flex h-[217px] items-center justify-center -mb-4">
<div id="spinner" class="flex h-54 items-center justify-center -mb-4">
<LoadingSpinner size="large" />
</div>
{:then people}

View File

@@ -30,7 +30,7 @@
</script>
<div class="mb-4 w-full">
<div class="flex h-[26px] place-items-center gap-1">
<div class="flex h-6.5 place-items-center gap-1">
<label class="font-medium text-primary text-sm" for="{name}-select">
{label}
</label>

View File

@@ -30,7 +30,7 @@
<div class="grid grid-cols-2">
<div>
<div class="flex h-[26px] place-items-center gap-1">
<div class="flex h-6.5 place-items-center gap-1">
<label class="font-medium text-primary text-sm" for={title}>
{title}
</label>

View File

@@ -28,7 +28,7 @@
<div class="flex place-items-center justify-between">
<div>
<div class="flex h-[26px] place-items-center gap-1">
<div class="flex h-6.5 place-items-center gap-1">
<label class="font-medium text-primary text-sm" for={title}>
{title}
</label>

View File

@@ -39,7 +39,7 @@
</script>
<div class="mb-4 w-full">
<div class="flex h-[26px] place-items-center gap-1">
<div class="flex h-6.5 place-items-center gap-1">
<label class="font-medium text-primary text-sm" for="{name}-select">{label}</label>
{#if isEdited}

View File

@@ -34,7 +34,7 @@
<div class="flex place-items-center justify-between">
<div class="me-2">
<div class="flex h-[26px] place-items-center gap-1">
<div class="flex h-6.5 place-items-center gap-1">
<label class="font-medium text-primary text-sm" for={sliderId}>
{title}
</label>

View File

@@ -30,7 +30,7 @@
</script>
<div class="mb-4 w-full">
<div class="flex h-[26px] place-items-center gap-1">
<div class="flex h-6.5 place-items-center gap-1">
<label class="font-medium text-primary text-sm" for={label}>{label}</label>
{#if required}
<div class="text-red-400">*</div>

View File

@@ -37,7 +37,7 @@
<div class={showSettingDescription ? 'grid grid-cols-2' : ''}>
{#if showSettingDescription}
<div>
<div class="flex h-[26px] place-items-center gap-1">
<div class="flex h-6.5 place-items-center gap-1">
<label class="font-medium text-primary text-sm" for={$t('language')}>
{$t('language')}
</label>

View File

@@ -94,7 +94,7 @@
<div class="flex justify-between w-full place-items-center place-content-center">
<div class="flex place-items-center place-content-center gap-1">
<div class="h-6 w-6">
<ImmichLogo noText class="h-[24px]" />
<ImmichLogo noText class="h-6" />
</div>
<p class="flex text-primary font-medium">
{$t('purchase_button_buy_immich')}
@@ -113,7 +113,7 @@
{#if showMessage}
<dialog
open
class="hidden sidebar:block w-[500px] absolute bottom-[75px] start-[255px] bg-gray-50 dark:border-gray-800 border border-gray-200 dark:bg-immich-dark-gray dark:text-white text-black rounded-3xl shadow-2xl px-8 py-6"
class="hidden sidebar:block w-125 absolute bottom-19 start-64 bg-gray-50 dark:border-gray-800 border border-gray-200 dark:bg-immich-dark-gray dark:text-white text-black rounded-3xl shadow-2xl px-8 py-6"
transition:fade={{ duration: 150 }}
onmouseover={() => (hoverMessage = true)}
onmouseleave={() => (hoverMessage = false)}
@@ -122,7 +122,7 @@
>
<div class="flex justify-between place-items-center">
<div class="h-10 w-10">
<ImmichLogo noText class="h-[32px]" />
<ImmichLogo noText class="h-8" />
</div>
<IconButton
shape="round"

View File

@@ -95,8 +95,8 @@
</div>
{#if uploadAsset.state === UploadState.STARTED}
<div class="text-black relative mt-[5px] h-[18px] w-full rounded-md bg-gray-300 dark:bg-gray-700">
<div class="h-[18px] rounded-md bg-immich-primary transition-all" style={`width: ${uploadAsset.progress}%`}></div>
<div class="text-black relative mt-[5px] h-4.5 w-full rounded-md bg-gray-300 dark:bg-gray-700">
<div class="h-4.5 rounded-md bg-immich-primary transition-all" style={`width: ${uploadAsset.progress}%`}></div>
<p class="absolute top-0.5 h-full w-full text-center text-white text-[10px]">
{#if uploadAsset.message === $t('asset_hashing')}
{uploadAsset.message}

View File

@@ -42,7 +42,7 @@
{#if showDetail}
<div
in:scale={{ duration: 250, easing: quartInOut }}
class="w-[325px] rounded-xl border border-gray-200 dark:border-subtle p-4 text-sm shadow-xs bg-subtle"
class="w-81 rounded-xl border border-gray-200 dark:border-subtle p-4 text-sm shadow-xs bg-subtle"
>
<div class="place-item-center mb-4 flex justify-between">
<div class="flex flex-col gap-1">
@@ -101,8 +101,8 @@
</div>
</div>
{#if showOptions}
<div class="immich-scrollbar mb-4 max-h-[400px] overflow-y-auto rounded-lg">
<div class="flex h-[26px] place-items-center gap-1">
<div class="immich-scrollbar mb-4 max-h-100 overflow-y-auto rounded-lg">
<div class="flex h-6.5 place-items-center gap-1">
<label class="immich-form-label" for="upload-concurrency">{$t('upload_concurrency')}</label>
</div>
<input

View File

@@ -61,7 +61,7 @@ describe('Sidebar component', () => {
const parent = screen.getByTestId('sidebar-parent');
// then
expect(parent.classList).toContain('sidebar:w-[16rem]'); // sets the initial width for page load
expect(parent.classList).toContain('sidebar:w-64'); // sets the initial width for page load
expect(parent.classList).toContain('w-[min(100vw,16rem)]');
expect(parent.classList).toContain('shadow-2xl');
});

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