mirror of
https://github.com/immich-app/immich.git
synced 2025-12-10 14:51:07 -08:00
Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f5ff36a1f8 | ||
|
|
b5efc9c16e | ||
|
|
1036076b0d | ||
|
|
c76324c611 | ||
|
|
0ddb92e1ec | ||
|
|
d08a520aa2 | ||
|
|
7bdf0f6c50 | ||
|
|
2b33a58448 | ||
|
|
b35f00f768 | ||
|
|
86cc7c3c73 | ||
|
|
5854cbbe97 | ||
|
|
ceb36a304d | ||
|
|
f5d7e5acca | ||
|
|
be15a84f9b | ||
|
|
32791e98c2 | ||
|
|
7ea443b3a9 | ||
|
|
c69786b039 | ||
|
|
5c7d5539ea | ||
|
|
3531856d1c | ||
|
|
4abaad548a | ||
|
|
61a2c3ace3 | ||
|
|
e9038193db | ||
|
|
3f5cd48a59 | ||
|
|
4cb094e7ae |
95
.github/workflows/build-mobile.yml
vendored
95
.github/workflows/build-mobile.yml
vendored
@@ -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()
|
||||
|
||||
3
.github/workflows/cli.yml
vendored
3
.github/workflows/cli.yml
vendored
@@ -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 }}
|
||||
|
||||
2
.github/workflows/fix-format.yml
vendored
2
.github/workflows/fix-format.yml
vendored
@@ -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
|
||||
|
||||
15
.github/workflows/prepare-release.yml
vendored
15
.github/workflows/prepare-release.yml
vendored
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
8
docs/static/archived-versions.json
vendored
8
docs/static/archived-versions.json
vendored
@@ -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"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "immich-e2e",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.2",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"type": "module",
|
||||
|
||||
@@ -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",
|
||||
|
||||
10
i18n/de.json
10
i18n/de.json
@@ -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 0–1. 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 0–1. 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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
96
i18n/hi.json
96
i18n/hi.json
@@ -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": "फ़ाइल का नाम या एक्सटेंशन",
|
||||
|
||||
@@ -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...",
|
||||
|
||||
@@ -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",
|
||||
|
||||
90
i18n/pt.json
90
i18n/pt.json
@@ -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}",
|
||||
|
||||
@@ -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": "Удалить сообщение?",
|
||||
|
||||
@@ -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",
|
||||
|
||||
25
i18n/ta.json
25
i18n/ta.json
@@ -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": "நேர மண்டலம்",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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": "管理密碼登入設定",
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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",
|
||||
]
|
||||
|
||||
|
||||
2
machine-learning/uv.lock
generated
2
machine-learning/uv.lock
generated
@@ -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" },
|
||||
]
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "fastlane"
|
||||
gem "cocoapods"
|
||||
gem "cocoapods"
|
||||
gem "abbrev" # Required for Ruby 3.4+
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
----
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -35,7 +35,8 @@ class SimilarPhotosActionButton extends ConsumerWidget {
|
||||
mediaType: AssetType.image,
|
||||
),
|
||||
);
|
||||
unawaited(context.router.popAndPush(const DriftSearchRoute()));
|
||||
|
||||
unawaited(context.navigateTo(const DriftSearchRoute()));
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -127,7 +127,7 @@ enum ActionButtonType {
|
||||
context.currentAlbum!.isShared,
|
||||
ActionButtonType.similarPhotos =>
|
||||
!context.isInLockedView && //
|
||||
context.asset.hasRemote,
|
||||
context.asset is RemoteAsset,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
2
mobile/openapi/README.md
generated
2
mobile/openapi/README.md
generated
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -10006,7 +10006,7 @@
|
||||
"info": {
|
||||
"title": "Immich",
|
||||
"description": "Immich API",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.2",
|
||||
"contact": {}
|
||||
},
|
||||
"tags": [],
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "immich",
|
||||
"version": "2.2.0",
|
||||
"version": "2.2.2",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
|
||||
@@ -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`);
|
||||
|
||||
@@ -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[]> {
|
||||
|
||||
@@ -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 })
|
||||
|
||||
@@ -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": {
|
||||
|
||||
78
web/src/lib/components/ApiKeyPermissionsPicker.svelte
Normal file
78
web/src/lib/components/ApiKeyPermissionsPicker.svelte
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
>
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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'}"
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user