Compare commits
165 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75edc6de0f | ||
|
|
780c5183e3 | ||
|
|
25a10784eb | ||
|
|
6e1d09fc32 | ||
|
|
73a2063d96 | ||
|
|
deb1e7f41f | ||
|
|
f45f719b9d | ||
|
|
325639b308 | ||
|
|
386eef046d | ||
|
|
db6b14361d | ||
|
|
719f074ccf | ||
|
|
646b912da8 | ||
|
|
b29c43d86a | ||
|
|
7ce64ecf05 | ||
|
|
9a332074c7 | ||
|
|
dd02f1025f | ||
|
|
d7bfab7b13 | ||
|
|
05cf5d57a9 | ||
|
|
f56eaae019 | ||
|
|
0d436db3ea | ||
|
|
6c8b29f326 | ||
|
|
23e76b0bd9 | ||
|
|
82e8cd0f8d | ||
|
|
87d84b922f | ||
|
|
04955a4123 | ||
|
|
54831878e0 | ||
|
|
08ed71e51e | ||
|
|
3a1d5de742 | ||
|
|
e15be5bf9a | ||
|
|
01afeefeb9 | ||
|
|
532bd6fe12 | ||
|
|
416e30ede2 | ||
|
|
ceb81d00fc | ||
|
|
8adca31c24 | ||
|
|
3cce43309c | ||
|
|
63ad802013 | ||
|
|
9313e70575 | ||
|
|
838ea56605 | ||
|
|
9ac087c59c | ||
|
|
f52e076cb3 | ||
|
|
8857d0b8df | ||
|
|
950989a85e | ||
|
|
a4c215751e | ||
|
|
2ca560ebf8 | ||
|
|
1f631eafce | ||
|
|
6f605d4a35 | ||
|
|
1918625be9 | ||
|
|
bdf35b6688 | ||
|
|
2ac54ce4bd | ||
|
|
96d75c9ad4 | ||
|
|
dac4020f27 | ||
|
|
a5f49b065c | ||
|
|
d5d0624311 | ||
|
|
8708867c1c | ||
|
|
8f11529a75 | ||
|
|
0aaeab124d | ||
|
|
1cc184ed10 | ||
|
|
830f4268c3 | ||
|
|
977740045a | ||
|
|
2a1dcbc28b | ||
|
|
21f8ab647f | ||
|
|
aef5a48fc6 | ||
|
|
434c1a0f20 | ||
|
|
5fd2496774 | ||
|
|
7411bcbb30 | ||
|
|
7d6d51f4a5 | ||
|
|
5777693fad | ||
|
|
b53cc4f9db | ||
|
|
9d57039274 | ||
|
|
12217bde8a | ||
|
|
37f802d1fe | ||
|
|
df1710f4cc | ||
|
|
0fec34d316 | ||
|
|
a0b8312ce4 | ||
|
|
8abe6909ca | ||
|
|
25cff6a748 | ||
|
|
243c98a02e | ||
|
|
c9a6820de7 | ||
|
|
7d586492f3 | ||
|
|
807bdfeda9 | ||
|
|
1f25df308a | ||
|
|
2efa8b6960 | ||
|
|
7c9d2018d8 | ||
|
|
ab90b01122 | ||
|
|
d04ef319b8 | ||
|
|
368142e79b | ||
|
|
7d45ae68a6 | ||
|
|
98bedcf1e5 | ||
|
|
3377fa4640 | ||
|
|
641c05c6fe | ||
|
|
e157a69d86 | ||
|
|
3d468c369c | ||
|
|
6c7679714b | ||
|
|
71d8567f18 | ||
|
|
cc6253ba38 | ||
|
|
3ea107be5a | ||
|
|
4ed96cf1bd | ||
|
|
9323cc76d9 | ||
|
|
da9b9c8c69 | ||
|
|
3c5c0ea68f | ||
|
|
2b988e1d5d | ||
|
|
8bcb2558b6 | ||
|
|
b8785a5b93 | ||
|
|
b00631d186 | ||
|
|
de5a6b2c35 | ||
|
|
3beb8193ae | ||
|
|
a2549c5bbd | ||
|
|
98a8be82e2 | ||
|
|
5c86e13239 | ||
|
|
10cb612fb1 | ||
|
|
a9a769d902 | ||
|
|
846e35f57e | ||
|
|
a3b9a0be3a | ||
|
|
2a3235f606 | ||
|
|
08b221c270 | ||
|
|
3102c3128f | ||
|
|
9ebed3c1b4 | ||
|
|
24d672a0ff | ||
|
|
e9f99302c1 | ||
|
|
5cdf7671ed | ||
|
|
4dab50c10a | ||
|
|
c416dd30e2 | ||
|
|
4ebc8870c2 | ||
|
|
bf3f4e560d | ||
|
|
4be55428d2 | ||
|
|
e9c9b7a3e2 | ||
|
|
2d2cfb0349 | ||
|
|
98998cccbc | ||
|
|
03d484aba2 | ||
|
|
88a2966666 | ||
|
|
e408e8ca4a | ||
|
|
9bfb4dfd06 | ||
|
|
7dc7281e69 | ||
|
|
87fea29e32 | ||
|
|
2cf42e867c | ||
|
|
83a2669ff5 | ||
|
|
824409351e | ||
|
|
d1ea6a897e | ||
|
|
5d3e8f17d1 | ||
|
|
78a5fe2d37 | ||
|
|
5ad4e5b614 | ||
|
|
000d0a08f4 | ||
|
|
917f1dea9f | ||
|
|
e309647f1b | ||
|
|
57136e48fb | ||
|
|
8c315dfeb1 | ||
|
|
6e9749d6c4 | ||
|
|
bf6f94f69f | ||
|
|
575154fdea | ||
|
|
857bbe3c3b | ||
|
|
0a0b255505 | ||
|
|
73b4b032b1 | ||
|
|
8234e44921 | ||
|
|
36197cca98 | ||
|
|
7a25d359b7 | ||
|
|
7cfb257c00 | ||
|
|
b660240059 | ||
|
|
125ec1e85f | ||
|
|
d31b35873f | ||
|
|
e1c520b9e7 | ||
|
|
1361f18964 | ||
|
|
0f00f22212 | ||
|
|
0d543bbb0a | ||
|
|
86b3bdb90b | ||
|
|
d47cdfb647 |
8
.github/workflows/build-mobile.yml
vendored
@@ -11,9 +11,15 @@ on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-sign-android:
|
||||
name: Build and sign Android
|
||||
# Skip when PR from a fork
|
||||
if: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
runs-on: macos-12
|
||||
|
||||
steps:
|
||||
@@ -24,7 +30,7 @@ jobs:
|
||||
github_ref="${{ github.sha }}"
|
||||
ref="${input_ref:-$github_ref}"
|
||||
echo "ref=$ref" >> $GITHUB_OUTPUT
|
||||
|
||||
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ steps.get-ref.outputs.ref }}
|
||||
|
||||
10
.github/workflows/cache-cleanup.yml
vendored
@@ -4,24 +4,28 @@ on:
|
||||
types:
|
||||
- closed
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
|
||||
- name: Cleanup
|
||||
run: |
|
||||
gh extension install actions/gh-actions-cache
|
||||
|
||||
|
||||
REPO=${{ github.repository }}
|
||||
BRANCH=${{ github.ref }}
|
||||
|
||||
echo "Fetching list of cache keys"
|
||||
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH -L 100 | cut -f 1 )
|
||||
|
||||
## Setting this to not fail the workflow while deleting cache keys.
|
||||
## Setting this to not fail the workflow while deleting cache keys.
|
||||
set +e
|
||||
echo "Deleting caches..."
|
||||
for cacheKey in $cacheKeysForPR
|
||||
|
||||
10
.github/workflows/codeql-analysis.yml
vendored
@@ -20,6 +20,10 @@ on:
|
||||
schedule:
|
||||
- cron: '20 13 * * 1'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
@@ -48,11 +52,11 @@ jobs:
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
@@ -61,7 +65,7 @@ jobs:
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
|
||||
4
.github/workflows/dispatch_sdk_update.yml
vendored
@@ -5,6 +5,10 @@ on:
|
||||
push:
|
||||
branches: ["main"]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
update-sdk-repos:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
16
.github/workflows/docker.yml
vendored
@@ -9,6 +9,10 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build_and_push:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -19,13 +23,17 @@ jobs:
|
||||
include:
|
||||
- context: "server"
|
||||
image: "immich-server"
|
||||
platforms: "linux/arm/v7,linux/amd64,linux/arm64"
|
||||
- context: "web"
|
||||
image: "immich-web"
|
||||
platforms: "linux/arm/v7,linux/amd64,linux/arm64"
|
||||
- context: "machine-learning"
|
||||
image: "immich-machine-learning"
|
||||
platforms: "linux/amd64,linux/arm64"
|
||||
- context: "nginx"
|
||||
image: "immich-proxy"
|
||||
|
||||
platforms: "linux/arm/v7,linux/amd64,linux/arm64"
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
@@ -34,7 +42,7 @@ jobs:
|
||||
uses: docker/setup-qemu-action@v2.1.0
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2.4.1
|
||||
uses: docker/setup-buildx-action@v2.5.0
|
||||
# Workaround to fix error:
|
||||
# failed to push: failed to copy: io: read/write on closed pipe
|
||||
# See https://github.com/docker/build-push-action/issues/761
|
||||
@@ -49,7 +57,7 @@ jobs:
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
# Skip when PR from a fork
|
||||
@@ -92,7 +100,7 @@ jobs:
|
||||
uses: docker/build-push-action@v4.0.0
|
||||
with:
|
||||
context: ${{ matrix.context }}
|
||||
platforms: linux/arm/v7,linux/amd64,linux/arm64
|
||||
platforms: ${{ matrix.platforms }}
|
||||
# Skip pushing when PR from a fork
|
||||
push: ${{ !github.event.pull_request.head.repo.fork }}
|
||||
cache-from: type=registry,ref=ghcr.io/${{ github.repository_owner }}/immich-build-cache:${{matrix.image}}
|
||||
|
||||
4
.github/workflows/github-repo-stats.yml
vendored
@@ -7,6 +7,10 @@ on:
|
||||
- cron: "0 23 * * *"
|
||||
workflow_dispatch: # Allow for running this manually.
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
j1:
|
||||
name: github-repo-stats
|
||||
|
||||
12
.github/workflows/prepare-release.yml
vendored
@@ -17,13 +17,17 @@ on:
|
||||
required: false
|
||||
type: boolean
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-root
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
bump_version:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
||||
outputs:
|
||||
ref: ${{ steps.push-tag.outputs.commit_long_sha }}
|
||||
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
@@ -42,7 +46,7 @@ jobs:
|
||||
message: "Version ${{ env.IMMICH_VERSION }}"
|
||||
tag: ${{ env.IMMICH_VERSION }}
|
||||
push: true
|
||||
|
||||
|
||||
build_mobile:
|
||||
uses: ./.github/workflows/build-mobile.yml
|
||||
needs: bump_version
|
||||
@@ -59,7 +63,7 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.ORG_RELEASE_TOKEN }}
|
||||
|
||||
|
||||
- name: Download APK
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
|
||||
4
.github/workflows/static_analysis.yml
vendored
@@ -5,6 +5,10 @@ on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
mobile-dart-analyze:
|
||||
name: Run Dart Code Analysis
|
||||
|
||||
178
.github/workflows/test.yml
vendored
@@ -5,6 +5,10 @@ on:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
e2e-tests:
|
||||
name: Run end-to-end test suites
|
||||
@@ -48,47 +52,149 @@ jobs:
|
||||
- name: Setup Flutter SDK
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: 'stable'
|
||||
flutter-version: '3.7.3'
|
||||
channel: "stable"
|
||||
flutter-version: "3.7.3"
|
||||
- name: Run tests
|
||||
working-directory: ./mobile
|
||||
run: flutter test
|
||||
|
||||
mobile-integration-tests:
|
||||
name: Run mobile end-to-end integration tests
|
||||
runs-on: macos-latest
|
||||
generated-api-up-to-date:
|
||||
name: Check generated files are up-to-date
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-java@v3
|
||||
- name: Run API generation
|
||||
run: cd server && npm ci && npm run api:generate
|
||||
- name: Find file changes
|
||||
uses: tj-actions/verify-changed-files@v13.1
|
||||
id: verify-changed-files
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '11'
|
||||
- name: Cache android SDK
|
||||
uses: actions/cache@v3
|
||||
id: android-sdk
|
||||
files: |
|
||||
mobile/openapi
|
||||
web/src/api/open-api
|
||||
- name: Verify files have not changed
|
||||
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||
run: |
|
||||
echo "ERROR: Generated files not up to date!"
|
||||
echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
||||
exit 1
|
||||
|
||||
generated-typeorm-migrations-up-to-date:
|
||||
name: Check generated TypeORM migrations are up-to-date
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: immich
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install server dependencies
|
||||
run: |
|
||||
cd server
|
||||
npm ci
|
||||
- name: Run existing migrations
|
||||
run: |
|
||||
cd server
|
||||
npm run typeorm:migrations:run
|
||||
- name: Generate new migrations
|
||||
continue-on-error: true
|
||||
run: |
|
||||
cd server
|
||||
npm run typeorm:migrations:generate ./libs/infra/src/db/migrations/TestMigration
|
||||
- name: Find file changes
|
||||
uses: tj-actions/verify-changed-files@v13.1
|
||||
id: verify-changed-files
|
||||
with:
|
||||
key: android-sdk
|
||||
path: |
|
||||
/usr/local/lib/android/
|
||||
~/.android
|
||||
- name: Setup Android SDK
|
||||
if: steps.android-sdk.outputs.cache-hit != 'true'
|
||||
uses: android-actions/setup-android@v2
|
||||
- name: Setup Flutter SDK
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: 'stable'
|
||||
flutter-version: '3.7.3'
|
||||
- name: Run integration tests
|
||||
uses: reactivecircus/android-emulator-runner@v2.27.0
|
||||
with:
|
||||
working-directory: ./mobile
|
||||
api-level: 29
|
||||
arch: x86_64
|
||||
profile: pixel
|
||||
target: default
|
||||
emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim
|
||||
disable-linux-hw-accel: false
|
||||
script: |
|
||||
flutter pub get
|
||||
flutter test integration_test
|
||||
files: |
|
||||
server/libs/infra/src/db/migrations/
|
||||
- name: Verify files have not changed
|
||||
if: steps.verify-changed-files.outputs.files_changed == 'true'
|
||||
run: |
|
||||
echo "ERROR: Generated files not up to date!"
|
||||
echo "Changed files: ${{ steps.verify-changed-files.outputs.changed_files }}"
|
||||
exit 1
|
||||
|
||||
# mobile-integration-tests:
|
||||
# name: Run mobile end-to-end integration tests
|
||||
# runs-on: macos-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v3
|
||||
# - uses: actions/setup-java@v3
|
||||
# with:
|
||||
# distribution: 'zulu'
|
||||
# java-version: '12.x'
|
||||
# cache: 'gradle'
|
||||
# - name: Cache android SDK
|
||||
# uses: actions/cache@v3
|
||||
# id: android-sdk
|
||||
# with:
|
||||
# key: android-sdk
|
||||
# path: |
|
||||
# /usr/local/lib/android/
|
||||
# ~/.android
|
||||
# - name: Cache Gradle
|
||||
# uses: actions/cache@v3
|
||||
# with:
|
||||
# path: |
|
||||
# ./mobile/build/
|
||||
# ./mobile/android/.gradle/
|
||||
# key: ${{ runner.os }}-flutter-${{ hashFiles('**/*.gradle*', 'pubspec.lock') }}
|
||||
# - name: Setup Android SDK
|
||||
# if: steps.android-sdk.outputs.cache-hit != 'true'
|
||||
# uses: android-actions/setup-android@v2
|
||||
# - name: AVD cache
|
||||
# uses: actions/cache@v3
|
||||
# id: avd-cache
|
||||
# with:
|
||||
# path: |
|
||||
# ~/.android/avd/*
|
||||
# ~/.android/adb*
|
||||
# key: avd-29
|
||||
# - name: create AVD and generate snapshot for caching
|
||||
# if: steps.avd-cache.outputs.cache-hit != 'true'
|
||||
# uses: reactivecircus/android-emulator-runner@v2.27.0
|
||||
# with:
|
||||
# working-directory: ./mobile
|
||||
# cores: 2
|
||||
# api-level: 29
|
||||
# arch: x86_64
|
||||
# profile: pixel
|
||||
# target: default
|
||||
# force-avd-creation: false
|
||||
# emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
# disable-animations: false
|
||||
# script: echo "Generated AVD snapshot for caching."
|
||||
# - name: Setup Flutter SDK
|
||||
# uses: subosito/flutter-action@v2
|
||||
# with:
|
||||
# channel: 'stable'
|
||||
# flutter-version: '3.7.3'
|
||||
# cache: true
|
||||
# - name: Run integration tests
|
||||
# uses: Wandalen/wretry.action@master
|
||||
# with:
|
||||
# action: reactivecircus/android-emulator-runner@v2.27.0
|
||||
# with: |
|
||||
# working-directory: ./mobile
|
||||
# cores: 2
|
||||
# api-level: 29
|
||||
# arch: x86_64
|
||||
# profile: pixel
|
||||
# target: default
|
||||
# force-avd-creation: false
|
||||
# emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
# disable-animations: true
|
||||
# script: |
|
||||
# flutter pub get
|
||||
# flutter test integration_test
|
||||
# attempt_limit: 3
|
||||
|
||||
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "mobile/.isar"]
|
||||
path = mobile/.isar
|
||||
url = https://github.com/isar/isar
|
||||
40
README.md
@@ -61,25 +61,25 @@ Spec: Free-tier Oracle VM - Amsterdam - 2.4Ghz quad-core ARM64 CPU, 24GB RAM
|
||||
|
||||
# Features
|
||||
|
||||
| Features | Mobile | Web |
|
||||
| ------------------------------------------- | ------- | --- |
|
||||
| Upload and view videos and photos | Yes | Yes |
|
||||
| Auto backup when the app is opened | Yes | N/A |
|
||||
| Selective album(s) for backup | Yes | N/A |
|
||||
| Download photos and videos to local device | Yes | Yes |
|
||||
| Multi-user support | Yes | Yes |
|
||||
| Album and Shared albums | Yes | Yes |
|
||||
| Scrubbable/draggable scrollbar | Yes | Yes |
|
||||
| Support RAW (HEIC, HEIF, DNG, Apple ProRaw) | Yes | Yes |
|
||||
| Metadata view (EXIF, map) | Yes | Yes |
|
||||
| Search by metadata, objects and image tags | Yes | No |
|
||||
| Administrative functions (user management) | N/A | Yes |
|
||||
| Background backup | Android | N/A |
|
||||
| Virtual scroll | Yes | Yes |
|
||||
| OAuth support | Yes | Yes |
|
||||
| LivePhoto backup and playback | iOS | Yes |
|
||||
| User-defined storage structure | Yes | Yes |
|
||||
| Public Sharing | N/A | Yes |
|
||||
| Features | Mobile | Web |
|
||||
| ------------------------------------------- | ------ | --- |
|
||||
| Upload and view videos and photos | Yes | Yes |
|
||||
| Auto backup when the app is opened | Yes | N/A |
|
||||
| Selective album(s) for backup | Yes | N/A |
|
||||
| Download photos and videos to local device | Yes | Yes |
|
||||
| Multi-user support | Yes | Yes |
|
||||
| Album and Shared albums | Yes | Yes |
|
||||
| Scrubbable/draggable scrollbar | Yes | Yes |
|
||||
| Support RAW (HEIC, HEIF, DNG, Apple ProRaw) | Yes | Yes |
|
||||
| Metadata view (EXIF, map) | Yes | Yes |
|
||||
| Search by metadata, objects and image tags | Yes | No |
|
||||
| Administrative functions (user management) | N/A | Yes |
|
||||
| Background backup | Yes | N/A |
|
||||
| Virtual scroll | Yes | Yes |
|
||||
| OAuth support | Yes | Yes |
|
||||
| LivePhoto backup and playback | iOS | Yes |
|
||||
| User-defined storage structure | Yes | Yes |
|
||||
| Public Sharing | N/A | Yes |
|
||||
|
||||
# Support the project
|
||||
|
||||
@@ -92,7 +92,7 @@ If you feel like this is the right cause and the app is something you are seeing
|
||||
## Donation
|
||||
|
||||
- [Monthly donation](https://github.com/sponsors/alextran1502) via GitHub Sponsors
|
||||
- [One-time donation](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) via Github Sponsors
|
||||
- [One-time donation](https://github.com/sponsors/alextran1502?frequency=one-time&sponsor=alextran1502) via GitHub Sponsors
|
||||
- [Librepay](https://liberapay.com/alex.tran1502/)
|
||||
- [buymeacoffee](https://www.buymeacoffee.com/altran1502)
|
||||
- Bitcoin: 1FvEp6P6NM8EZEkpGUFAN2LqJ1gxusNxZX
|
||||
|
||||
@@ -17,3 +17,5 @@ ENABLE_MAPBOX=false
|
||||
# WEB
|
||||
MAPBOX_KEY=
|
||||
VITE_SERVER_ENDPOINT=http://localhost:2283/api
|
||||
|
||||
TYPESENSE_ENABLED=false
|
||||
|
||||
@@ -23,6 +23,7 @@ services:
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
- typesense
|
||||
|
||||
immich-machine-learning:
|
||||
container_name: immich_machine_learning
|
||||
@@ -30,18 +31,20 @@ services:
|
||||
build:
|
||||
context: ../machine-learning
|
||||
dockerfile: Dockerfile
|
||||
target: builder
|
||||
command: npm run start:dev
|
||||
command: python main.py
|
||||
ports:
|
||||
- 3003:3003
|
||||
volumes:
|
||||
- ../machine-learning:/usr/src/app
|
||||
- ../machine-learning/src:/usr/src/app
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- /usr/src/app/node_modules
|
||||
- model-cache:/cache
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
depends_on:
|
||||
- database
|
||||
restart: always
|
||||
|
||||
immich-microservices:
|
||||
container_name: immich_microservices
|
||||
@@ -62,6 +65,7 @@ services:
|
||||
depends_on:
|
||||
- database
|
||||
- immich-server
|
||||
- typesense
|
||||
|
||||
immich-web:
|
||||
container_name: immich_web
|
||||
@@ -87,6 +91,17 @@ services:
|
||||
depends_on:
|
||||
- immich-server
|
||||
|
||||
typesense:
|
||||
container_name: immich_typesense
|
||||
image: typesense/typesense:0.24.0
|
||||
environment:
|
||||
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
|
||||
- TYPESENSE_DATA_DIR=/data
|
||||
logging:
|
||||
driver: none
|
||||
volumes:
|
||||
- tsdata:/data
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2
|
||||
@@ -126,3 +141,5 @@ services:
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
model-cache:
|
||||
tsdata:
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
immich-server:
|
||||
container_name: immich_server
|
||||
image: altran1502/immich-server:staging
|
||||
entrypoint: ["/bin/sh", "./start-server.sh"]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
restart: always
|
||||
|
||||
immich-microservices:
|
||||
container_name: immich_microservices
|
||||
image: altran1502/immich-server:staging
|
||||
entrypoint: ["/bin/sh", "./start-microservices.sh"]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
restart: always
|
||||
|
||||
immich-machine-learning:
|
||||
container_name: immich_machine_learning
|
||||
image: altran1502/immich-machine-learning:staging
|
||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- database
|
||||
restart: always
|
||||
|
||||
immich-web:
|
||||
container_name: immich_web
|
||||
image: altran1502/immich-web:staging
|
||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
# Rename these values for svelte public interface
|
||||
- PUBLIC_IMMICH_SERVER_URL=${IMMICH_SERVER_URL}
|
||||
- PUBLIC_IMMICH_API_URL_EXTERNAL=${IMMICH_API_URL_EXTERNAL}
|
||||
restart: always
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2
|
||||
restart: always
|
||||
|
||||
database:
|
||||
container_name: immich_postgres
|
||||
image: postgres:14
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
POSTGRES_USER: ${DB_USERNAME}
|
||||
POSTGRES_DB: ${DB_DATABASE_NAME}
|
||||
PG_DATA: /var/lib/postgresql/data
|
||||
volumes:
|
||||
- pgdata:/var/lib/postgresql/data
|
||||
restart: always
|
||||
|
||||
immich-proxy:
|
||||
container_name: immich_proxy
|
||||
image: altran1502/immich-proxy:staging
|
||||
environment:
|
||||
# Make sure these values get passed through from the env file
|
||||
- IMMICH_SERVER_URL
|
||||
- IMMICH_WEB_URL
|
||||
ports:
|
||||
- 2283:8080
|
||||
logging:
|
||||
driver: none
|
||||
depends_on:
|
||||
- immich-server
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
@@ -1,4 +1,4 @@
|
||||
version: '3.8'
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
immich-server-test:
|
||||
@@ -9,7 +9,7 @@ services:
|
||||
target: builder
|
||||
command: npm run test:e2e
|
||||
expose:
|
||||
- '3000'
|
||||
- "3000"
|
||||
volumes:
|
||||
- ../server:/usr/src/app
|
||||
- /usr/src/app/node_modules
|
||||
@@ -17,6 +17,7 @@ services:
|
||||
- .env.test
|
||||
environment:
|
||||
- NODE_ENV=development
|
||||
- TYPESENSE_ENABLED=false
|
||||
depends_on:
|
||||
- immich-redis-test
|
||||
- immich-database-test
|
||||
|
||||
@@ -3,8 +3,8 @@ version: "3.8"
|
||||
services:
|
||||
immich-server:
|
||||
container_name: immich_server
|
||||
image: altran1502/immich-server:release
|
||||
entrypoint: ["/bin/sh", "./start-server.sh"]
|
||||
image: ghcr.io/immich-app/immich-server:release
|
||||
entrypoint: [ "/bin/sh", "./start-server.sh" ]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
env_file:
|
||||
@@ -14,12 +14,13 @@ services:
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
- typesense
|
||||
restart: always
|
||||
|
||||
immich-microservices:
|
||||
container_name: immich_microservices
|
||||
image: altran1502/immich-server:release
|
||||
entrypoint: ["/bin/sh", "./start-microservices.sh"]
|
||||
image: ghcr.io/immich-app/immich-server:release
|
||||
entrypoint: [ "/bin/sh", "./start-microservices.sh" ]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
env_file:
|
||||
@@ -29,30 +30,40 @@ services:
|
||||
depends_on:
|
||||
- redis
|
||||
- database
|
||||
- typesense
|
||||
restart: always
|
||||
|
||||
immich-machine-learning:
|
||||
container_name: immich_machine_learning
|
||||
image: altran1502/immich-machine-learning:release
|
||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||
image: ghcr.io/immich-app/immich-machine-learning:release
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- model-cache:/cache
|
||||
env_file:
|
||||
- .env
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
depends_on:
|
||||
- database
|
||||
restart: always
|
||||
|
||||
immich-web:
|
||||
container_name: immich_web
|
||||
image: altran1502/immich-web:release
|
||||
entrypoint: ["/bin/sh", "./entrypoint.sh"]
|
||||
image: ghcr.io/immich-app/immich-web:release
|
||||
entrypoint: [ "/bin/sh", "./entrypoint.sh" ]
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
|
||||
typesense:
|
||||
container_name: immich_typesense
|
||||
image: typesense/typesense:0.24.0
|
||||
environment:
|
||||
- TYPESENSE_API_KEY=${TYPESENSE_API_KEY}
|
||||
- TYPESENSE_DATA_DIR=/data
|
||||
logging:
|
||||
driver: none
|
||||
volumes:
|
||||
- tsdata:/data
|
||||
|
||||
redis:
|
||||
container_name: immich_redis
|
||||
image: redis:6.2
|
||||
@@ -74,7 +85,7 @@ services:
|
||||
|
||||
immich-proxy:
|
||||
container_name: immich_proxy
|
||||
image: altran1502/immich-proxy:release
|
||||
image: ghcr.io/immich-app/immich-proxy:release
|
||||
environment:
|
||||
# Make sure these values get passed through from the env file
|
||||
- IMMICH_SERVER_URL
|
||||
@@ -89,3 +100,5 @@ services:
|
||||
|
||||
volumes:
|
||||
pgdata:
|
||||
model-cache:
|
||||
tsdata:
|
||||
|
||||
@@ -17,6 +17,11 @@ DB_DATABASE_NAME=immich
|
||||
REDIS_HOSTNAME=immich_redis
|
||||
|
||||
# Optional Redis settings:
|
||||
|
||||
# Note: these parameters are not automatically passed to the Redis Container
|
||||
# to do so, please edit the docker-compose.yml file as well. Redis is not configured
|
||||
# via environment variables, only redis.conf or the command line
|
||||
|
||||
# REDIS_PORT=6379
|
||||
# REDIS_DBINDEX=0
|
||||
# REDIS_PASSWORD=
|
||||
@@ -30,6 +35,13 @@ REDIS_HOSTNAME=immich_redis
|
||||
|
||||
UPLOAD_LOCATION=absolute_location_on_your_machine_where_you_want_to_store_the_backup
|
||||
|
||||
|
||||
###################################################################################
|
||||
# Typesense
|
||||
###################################################################################
|
||||
TYPESENSE_API_KEY=some-random-text
|
||||
# TYPESENSE_ENABLED=false
|
||||
|
||||
###################################################################################
|
||||
# Reverse Geocoding
|
||||
#
|
||||
@@ -76,4 +88,4 @@ IMMICH_MACHINE_LEARNING_URL=http://immich-machine-learning:3003
|
||||
# Examples: http://localhost:3001, http://immich-api.example.com, etc
|
||||
####################################################################################
|
||||
|
||||
#IMMICH_API_URL_EXTERNAL=http://localhost:3001
|
||||
#IMMICH_API_URL_EXTERNAL=http://localhost:3001
|
||||
|
||||
@@ -16,6 +16,19 @@ sidebar_position: 7
|
||||
|
||||
Immich doesn't have the mechanism to sync an existing directory with the server. There is however, a helper CLI tool to help you bulk upload the existing photos and videos to the server. You can find the guide to use the CLI tool [here](/docs/features/bulk-upload.md).
|
||||
|
||||
### Why does my uploaded photo show up with the wrong date or time in Immich?
|
||||
|
||||
When a photo is initially uploaded Immich uses the create date of the file to determine where it belongs in the timeline. After that, background jobs will run that extract [exif metadata](https://en.wikipedia.org/wiki/Exif), including the CreateDate, to provide a more accurate date for the photo. If that is not available it will fallback to the modified date. If you want to ensure your photo has the right date, check the exif metadata before uploading.
|
||||
|
||||
If the timezone is incorrect in an uploaded photo, check the ``DateTimeOriginal`` exif field of the uploaded file. Immich uses the very competent library [exiftool-vendored.js](https://github.com/photostructure/exiftool-vendored.js#dates) to handle timezone parsing, but in some cases (like photos taken with DSLR cameras) it has to fallback on the local timezone. If you are using docker, this fallback will be UTC. (Note that even the photo backup app that can't be named [has the same bug!](https://photo.stackexchange.com/a/126978)) In Immich, it is possible to change this assumed fallback timezone system-wide by setting the timezone in the microservices docker container. You might need to run the "Extract Metadata" job after to effect the change.
|
||||
|
||||
As an example, the following modification of ```docker-compose.yml``` will set the timezone of the microservices container to be ``Europe/Stockholm``
|
||||
|
||||
```
|
||||
environment:
|
||||
- TZ=Europe/Stockholm # <---- Add this line in the microservices config
|
||||
```
|
||||
|
||||
### Why doesn't Immich watch an existing photo gallery directory?
|
||||
|
||||
The initial approach of Immich is to become a backup tool, primarily for mobile device usage. Thus, all the assets must be uploaded from the mobile client. The app was architectured to perform that job well.
|
||||
|
||||
BIN
docs/docs/administration/img/admin-jobs.png
Normal file
|
After Width: | Height: | Size: 111 KiB |
@@ -2,22 +2,8 @@
|
||||
|
||||
Several Immich functionalities are implemented as jobs, which run in the background. To view the status of a job navigate to the Administration Screen, and then the `Jobs` page.
|
||||
|
||||

|
||||
|
||||
## Generate Thumbnails
|
||||
|
||||

|
||||
|
||||
|
||||
## Extract Exif
|
||||
|
||||

|
||||
|
||||
## Detect Objects
|
||||
|
||||

|
||||
|
||||
## Storage Migration
|
||||
|
||||
This job can be run after changing the [Storage Template](/docs/administration/storage-template.mdx), in order to apply the change to the existing library.
|
||||
|
||||

|
||||
:::info
|
||||
Storage Migration job can be run after changing the [Storage Template](/docs/administration/storage-template.mdx), in order to apply the change to the existing library.
|
||||
:::
|
||||
|
||||
@@ -28,6 +28,7 @@ Immich is a full-stack [TypeScript](https://www.typescriptlang.org/) application
|
||||
- [Nest.js](https://nestjs.com/)
|
||||
- [TypeORM](https://typeorm.io/) for database management.
|
||||
- [Jest](https://jestjs.io/) for testing.
|
||||
- [Python](https://www.python.org/) for Machine Learning.
|
||||
|
||||
### Database
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 691 KiB After Width: | Height: | Size: 242 KiB |
@@ -4,33 +4,31 @@ A guide on how the foreground and background automatic backup works.
|
||||
|
||||
<img src={require('./img/background-foreground-backup.png').default} width="50%" title="Foreground&Background Backup" />
|
||||
|
||||
On iOS, there is only one option for automatic backup
|
||||
|
||||
- [Automatic Backup](#automatic-backup)
|
||||
- [Foreground backup](#foreground-backup)
|
||||
|
||||
On Android, there are two options for automatic backup
|
||||
|
||||
- [Automatic Backup](#automatic-backup)
|
||||
- [Foreground backup](#foreground-backup)
|
||||
- [Background backup](#background-backup)
|
||||
|
||||
## Foreground backup
|
||||
|
||||
If foreground backup is enabled: whenever the app is opened or resumed, it will check if any photos or videos in the selected album(s) have yet to be uploaded to the cloud (the remainder count). If there are any, they will be uploaded.
|
||||
|
||||
## Background backup
|
||||
|
||||
Background backup is only available on Android thanks to the contribution effort of [@zoodyy](https://github.com/zoodyy).
|
||||
Background backup is available thanks to the contribution effort of [@zoodyy](https://github.com/zoodyy) and [@martyfuhry](https://github.com/martyfuhry).
|
||||
|
||||
If background backup is enabled. The app will periodically check if there are any new photos or videos in the selected album(s) to be uploaded to the cloud. If there are, it will upload them to the cloud in the background.
|
||||
|
||||
A native Android notification shows up when the background upload is in progress. You can further customize the notification by going to the app's settings.
|
||||
|
||||
:::info Note
|
||||
|
||||
#### General
|
||||
- The app must be in the background for the backup worker to start running.
|
||||
- It is a well-known problem that some Android models are very strict with battery optimization settings, which can cause a problem with the background worker. Please visit [Don't kill my app](https://dontkillmyapp.com/) for a guide on disabling this setting on your phone.
|
||||
- If you reopen the app and the first page you see is the backup page, the counts will not reflect the background uploaded result. You have to navigate out of the page and come back to see the updated counts.
|
||||
|
||||
#### Android
|
||||
- It is a well-known problem that some Android models are very strict with battery optimization settings, which can cause a problem with the background worker. Please visit [Don't kill my app](https://dontkillmyapp.com/) for a guide on disabling this setting on your phone.
|
||||
|
||||
#### iOS
|
||||
- You must enable **Background App Refresh** for the app to work in the background. You can enable it in the Settings app under General > Background App Refresh.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src={require('./img/background-app-refresh.png').default} width="30%" title="background-app-refresh" />
|
||||
</div>
|
||||
|
||||
:::
|
||||
|
||||
@@ -17,23 +17,29 @@ npm i -g immich
|
||||
|
||||
## Quick Start
|
||||
|
||||
Specify user's credentials, Immich's server address and port, and the directory you would like to upload videos/photos from.
|
||||
Specify user's credential, Immich's server address and port and the directory you would like to upload videos/photos from.
|
||||
|
||||
```bash
|
||||
immich upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api -d your/target/directory
|
||||
```
|
||||
immich upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api file1.jpg file2.jpg
|
||||
```
|
||||
|
||||
By default, subfolders are not included. To upload a directory including subfolder, use the --recursive option:
|
||||
|
||||
```
|
||||
immich upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api --recursive directory/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Parameters
|
||||
### Options
|
||||
|
||||
| Parameter | Description |
|
||||
| ---------------- | ------------------------------------------------------------------- |
|
||||
| --yes / -y | Assume yes on all interactive prompts |
|
||||
| --recursive / -r | Include subfolders |
|
||||
| --delete / -da | Delete local assets after upload |
|
||||
| --key / -k | User's API key |
|
||||
| --server / -s | Immich's server address |
|
||||
| --directory / -d | Directory to upload from |
|
||||
| --threads / -t | Number of threads to use (Default 5) |
|
||||
| --album/ -al | Create albums for assets based on the parent folder or a given name |
|
||||
|
||||
@@ -92,5 +98,5 @@ npm run build
|
||||
```
|
||||
|
||||
```bash title="Run the command"
|
||||
node bin/index.js upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api -d your/target/directory
|
||||
node bin/index.js upload --key HFEJ38DNSDUEG --server http://192.168.1.216:2283/api --recursive your/asset/directory
|
||||
```
|
||||
|
||||
BIN
docs/docs/features/img/background-app-refresh.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 16 KiB |
@@ -46,6 +46,11 @@ DB_DATABASE_NAME=immich
|
||||
REDIS_HOSTNAME=immich_redis
|
||||
|
||||
# Optional Redis settings:
|
||||
|
||||
# Note: these parameters are not automatically passed to the Redis Container
|
||||
# to do so, please edit the docker-compose.yml file as well. Redis is not configured
|
||||
# via environment variables, only redis.conf or the command line
|
||||
|
||||
# REDIS_PORT=6379
|
||||
# REDIS_DBINDEX=0
|
||||
# REDIS_PASSWORD=
|
||||
|
||||
@@ -20,28 +20,3 @@ You can also use Podman to run the application. However, additional configuratio
|
||||
- **OS**: Preferred unix-based operating system (Ubuntu, Debian, MacOS, etc). Windows works too, with [Docker Desktop on Windows](https://docs.docker.com/desktop/install/windows-install/)
|
||||
- **RAM**: At least 2GB, preferred 4GB.
|
||||
- **CPU**: At least 2 cores, preferred 4 cores.
|
||||
|
||||
:::info Machine Learning on older CPU
|
||||
|
||||
The TensorFlow version used by Immich doesn't run on older CPU architectures. It requires a CPU with AVX and AVX2 instruction sets. If you encounter the error `illegal instruction core dump` check your CPU flags with the command below and make sure you see `avx` and `avx2`:
|
||||
|
||||
```bash
|
||||
grep -E 'avx2?' /proc/cpuinfo
|
||||
```
|
||||
|
||||
#### Promox
|
||||
|
||||
If you are running virtualization in Proxmox, the CPU type of the VM is probably configured incorrectly.
|
||||
|
||||
You need to change the CPU type from `kvm64` to `host` under VMs hardware tab.
|
||||
|
||||
`Hardware > Processors > Edit > Advanced > Type (dropdown menu) > host`
|
||||
|
||||
#### Other platforms
|
||||
|
||||
You can use the machine learning image that is built for Non-AVX CPU. The image is community maintained and can be found in the repository below
|
||||
|
||||
https://github.com/bertmelis/immich-machine-learning-no-avx
|
||||
|
||||
Otherwise, you can safely remove the `immich-machine-learning` service if you do not intend to use Immich's object detection features. Simply remove or comment out the declaration of the service in your compose file.
|
||||
:::
|
||||
|
||||
@@ -18,7 +18,7 @@ Immich can easily be installed and updated on Unraid via:
|
||||
|
||||
In order to install Immich from the Unraid CA, you will need an existing Redis and PostgreSQL 14 container, If you do not already have Redis or PostgreSQL you can install them from the Unraid CA, just make sure you choose PostgreSQL **14**.
|
||||
|
||||
Once you have Redis and PostgreSQL running, search for Immich on the Unraid CA, Choose either of the templates listed and fill out the example variables.
|
||||
Once you have Redis and PostgreSQL running, search for Immich on the Unraid CA, choose either of the templates listed and fill out the example variables.
|
||||
|
||||
For more information about setting up the community image see [here](https://github.com/imagegenius/docker-immich#application-setup)
|
||||
|
||||
@@ -71,7 +71,7 @@ alt="Select Plugins > Compose.Manager > Add New Stack > Label it Immich"
|
||||
</details>
|
||||
5. Click "**Save Changes**", you will be promoted to edit stack UI labels, just leave this blank and click "**Ok**"
|
||||
6. Select the cog ⚙️ next to Immich, click "**Edit Stack**", then click "**Env File**"
|
||||
7. Past the entire contents of the [Immich example.env](https://github.com/immich-app/immich/releases/latest/download/example.env) file into the Unraid editor, then **before saving** edit the following:
|
||||
7. Paste the entire contents of the [Immich example.env](https://github.com/immich-app/immich/releases/latest/download/example.env) file into the Unraid editor, then **before saving** edit the following:
|
||||
|
||||
- `UPLOAD_LOCATION`: Create a folder in your Images Unraid share and place the **absolute** location here > For example my _"images"_ share has a folder within it called _"immich"_. If I browse to this directory in the terminal and type `pwd` the output is `/mnt/user/images/immich`. This is the exact value I need to enter as my `UPLOAD_LOCATION`
|
||||
|
||||
@@ -143,8 +143,8 @@ alt="Immich update notification"
|
||||
/>
|
||||
|
||||
1. Go to the "**Docker**" tab and scroll to the Compose section
|
||||
2. Next to Immich click the "**Update Stack**" button and Unraid will begin to update all Immmich related containers
|
||||
> Note: **Do not** select Compose Down first, it is unecessary.
|
||||
2. Next to Immich click the "**Update Stack**" button and Unraid will begin to update all Immich related containers
|
||||
> Note: **Do not** select Compose Down first, it is unnecessary.
|
||||
3. Once complete you will see a "_Connection Closed_" message, select "**Done**".
|
||||
<img
|
||||
src={require('./img/unraid11.png').default}
|
||||
|
||||
|
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 52 KiB |
@@ -34,6 +34,8 @@ download:
|
||||
locale_code: pt-BR
|
||||
- file: mobile/assets/i18n/pl-PL.json
|
||||
locale_code: pl-PL
|
||||
- file: mobile/assets/i18n/sv-SE.json
|
||||
locale_code: sv-SE
|
||||
- file: mobile/assets/i18n/sk-SK.json
|
||||
locale_code: sk-SK
|
||||
- file: mobile/assets/i18n/zh-CN.json
|
||||
@@ -42,3 +44,5 @@ download:
|
||||
locale_code: ru-RU
|
||||
- file: mobile/assets/i18n/cs-CZ.json
|
||||
locale_code: cs-CZ
|
||||
- file: mobile/assets/i18n/no-NO.json
|
||||
locale_code: no-NO
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
node_modules/
|
||||
upload/
|
||||
dist/
|
||||
|
||||
venv/
|
||||
@@ -1,24 +0,0 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
},
|
||||
};
|
||||
41
machine-learning/.gitignore
vendored
@@ -1,37 +1,4 @@
|
||||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
pnpm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Tests
|
||||
/coverage
|
||||
/.nyc_output
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
upload/
|
||||
upload/
|
||||
venv/
|
||||
__pycache__/
|
||||
model-cache/
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
@@ -1,34 +1,25 @@
|
||||
FROM node:16-bullseye-slim as builder
|
||||
FROM python:3.10 as builder
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ENV PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PIP_NO_CACHE_DIR=true
|
||||
|
||||
RUN python -m venv /opt/venv
|
||||
RUN /opt/venv/bin/pip install --pre torch -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html
|
||||
RUN /opt/venv/bin/pip install transformers tqdm numpy scikit-learn scipy nltk sentencepiece flask Pillow gunicorn
|
||||
RUN /opt/venv/bin/pip install --no-deps sentence-transformers
|
||||
|
||||
FROM python:3.10-slim
|
||||
|
||||
COPY --from=builder /opt/venv /opt/venv
|
||||
|
||||
ENV TRANSFORMERS_CACHE=/cache \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
PYTHONUNBUFFERED=1 \
|
||||
PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install gcc g++ make cmake python3 python3-pip -y
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
|
||||
RUN npm ci
|
||||
RUN npm rebuild @tensorflow/tfjs-node --build-from-source
|
||||
|
||||
COPY . .
|
||||
|
||||
FROM builder as prod
|
||||
|
||||
RUN npm run build
|
||||
RUN npm prune --omit=dev
|
||||
|
||||
FROM node:16-bullseye-slim
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
COPY --from=prod /usr/src/app/node_modules ./node_modules
|
||||
COPY --from=prod /usr/src/app/dist ./dist
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
COPY entrypoint.sh ./
|
||||
|
||||
# CMD [ "node", "dist/main" ]
|
||||
CMD ["gunicorn", "src.main:server"]
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Hau Tran
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
# Microservices for Immich
|
||||
# Immich Machine Learning
|
||||
|
||||
## Image Classifier
|
||||
- Object Detection
|
||||
- Image Classification
|
||||
@@ -1,4 +0,0 @@
|
||||
#! /bin/sh
|
||||
# npm run typeorm migration:run
|
||||
# npm run start:prod
|
||||
exec node dist/main.js
|
||||
29
machine-learning/gunicorn.conf.py
Normal file
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Gunicorn configuration options.
|
||||
https://docs.gunicorn.org/en/stable/settings.html
|
||||
"""
|
||||
import os
|
||||
|
||||
|
||||
# Set the bind address based on the env
|
||||
port = os.getenv("MACHINE_LEARNING_PORT") or "3003"
|
||||
listen_ip = os.getenv("MACHINE_LEARNING_IP") or "0.0.0.0"
|
||||
bind = [f"{listen_ip}:{port}"]
|
||||
|
||||
# Preload the Flask app / models etc. before starting the server
|
||||
preload_app = True
|
||||
|
||||
# Logging settings - log to stdout and set log level
|
||||
accesslog = "-"
|
||||
loglevel = os.getenv("MACHINE_LEARNING_LOG_LEVEL") or "info"
|
||||
|
||||
# Worker settings
|
||||
# ----------------------
|
||||
# It is important these are chosen carefully as per
|
||||
# https://pythonspeed.com/articles/gunicorn-in-docker/
|
||||
# Otherwise we get workers failing to respond to heartbeat checks,
|
||||
# especially as requests take a long time to complete.
|
||||
workers = 2
|
||||
threads = 4
|
||||
worker_tmp_dir = "/dev/shm"
|
||||
timeout = 60
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src"
|
||||
}
|
||||
16346
machine-learning/package-lock.json
generated
@@ -1,71 +0,0 @@
|
||||
{
|
||||
"name": "nest_microservices",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"prebuild": "rimraf dist",
|
||||
"build": "nest build",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"start": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^8.0.0",
|
||||
"@nestjs/core": "^8.0.0",
|
||||
"@tensorflow-models/coco-ssd": "^2.2.2",
|
||||
"@tensorflow-models/mobilenet": "^2.1.0",
|
||||
"@tensorflow/tfjs-node": "^3.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^8.2.4",
|
||||
"@nestjs/schematics": "^8.0.0",
|
||||
"@nestjs/testing": "^8.0.0",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/jest": "27.4.1",
|
||||
"@types/node": "^16.0.0",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^27.2.5",
|
||||
"prettier": "^2.3.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"source-map-support": "^0.5.20",
|
||||
"supertest": "^6.1.3",
|
||||
"ts-jest": "^27.0.3",
|
||||
"ts-loader": "^9.2.3",
|
||||
"ts-node": "^10.0.0",
|
||||
"tsconfig-paths": "^3.10.1",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"**/*.(t|j)s"
|
||||
],
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ImageClassifierModule } from './image-classifier/image-classifier.module';
|
||||
import { ObjectDetectionModule } from './object-detection/object-detection.module';
|
||||
|
||||
@Module({
|
||||
imports: [ImageClassifierModule, ObjectDetectionModule],
|
||||
controllers: [],
|
||||
providers: [],
|
||||
})
|
||||
export class AppModule {}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { Body, Controller, Post } from '@nestjs/common';
|
||||
import { ImageClassifierService } from './image-classifier.service';
|
||||
|
||||
@Controller('image-classifier')
|
||||
export class ImageClassifierController {
|
||||
constructor(
|
||||
private readonly imageClassifierService: ImageClassifierService,
|
||||
) { }
|
||||
|
||||
@Post('/tag-image')
|
||||
async tagImage(@Body('thumbnailPath') thumbnailPath: string) {
|
||||
return await this.imageClassifierService.tagImage(thumbnailPath);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ImageClassifierService } from './image-classifier.service';
|
||||
import { ImageClassifierController } from './image-classifier.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [ImageClassifierController],
|
||||
providers: [ImageClassifierService],
|
||||
})
|
||||
export class ImageClassifierModule {}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import * as mobilenet from '@tensorflow-models/mobilenet';
|
||||
import * as cocoSsd from '@tensorflow-models/coco-ssd';
|
||||
import * as tf from '@tensorflow/tfjs-node';
|
||||
import * as fs from 'fs';
|
||||
|
||||
@Injectable()
|
||||
export class ImageClassifierService {
|
||||
private readonly MOBILENET_VERSION = 2;
|
||||
private readonly MOBILENET_ALPHA = 1.0;
|
||||
|
||||
private mobileNetModel: mobilenet.MobileNet;
|
||||
|
||||
constructor() {
|
||||
Logger.log(
|
||||
`Running Node TensorFlow Version : ${tf.version['tfjs']}`,
|
||||
'ImageClassifier',
|
||||
);
|
||||
mobilenet
|
||||
.load({
|
||||
version: this.MOBILENET_VERSION,
|
||||
alpha: this.MOBILENET_ALPHA,
|
||||
})
|
||||
.then((mobilenetModel) => (this.mobileNetModel = mobilenetModel));
|
||||
}
|
||||
|
||||
async tagImage(thumbnailPath: string) {
|
||||
try {
|
||||
const isExist = fs.existsSync(thumbnailPath);
|
||||
if (isExist) {
|
||||
const tags = [];
|
||||
const image = fs.readFileSync(thumbnailPath);
|
||||
const decodedImage = tf.node.decodeImage(image, 3) as tf.Tensor3D;
|
||||
const predictions = await this.mobileNetModel.classify(decodedImage);
|
||||
|
||||
for (const prediction of predictions) {
|
||||
if (prediction.probability >= 0.1) {
|
||||
tags.push(...prediction.className.split(',').map((e) => e.trim()));
|
||||
}
|
||||
}
|
||||
|
||||
tf.dispose(decodedImage);
|
||||
return tags;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Error reading file ', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
machine-learning/src/main.py
Normal file
@@ -0,0 +1,73 @@
|
||||
import os
|
||||
from flask import Flask, request
|
||||
from transformers import pipeline
|
||||
from sentence_transformers import SentenceTransformer, util
|
||||
from PIL import Image
|
||||
|
||||
is_dev = os.getenv('NODE_ENV') == 'development'
|
||||
server_port = os.getenv('MACHINE_LEARNING_PORT', 3003)
|
||||
server_host = os.getenv('MACHINE_LEARNING_HOST', '0.0.0.0')
|
||||
|
||||
classification_model = os.getenv('MACHINE_LEARNING_CLASSIFICATION_MODEL', 'microsoft/resnet-50')
|
||||
object_model = os.getenv('MACHINE_LEARNING_OBJECT_MODEL', 'hustvl/yolos-tiny')
|
||||
clip_image_model = os.getenv('MACHINE_LEARNING_CLIP_IMAGE_MODEL', 'clip-ViT-B-32')
|
||||
clip_text_model = os.getenv('MACHINE_LEARNING_CLIP_TEXT_MODEL', 'clip-ViT-B-32')
|
||||
|
||||
_model_cache = {}
|
||||
def _get_model(model, task=None):
|
||||
global _model_cache
|
||||
key = '|'.join([model, str(task)])
|
||||
if key not in _model_cache:
|
||||
if task:
|
||||
_model_cache[key] = pipeline(model=model, task=task)
|
||||
else:
|
||||
_model_cache[key] = SentenceTransformer(model)
|
||||
return _model_cache[key]
|
||||
|
||||
server = Flask(__name__)
|
||||
|
||||
@server.route("/ping")
|
||||
def ping():
|
||||
return "pong"
|
||||
|
||||
@server.route("/object-detection/detect-object", methods=['POST'])
|
||||
def object_detection():
|
||||
model = _get_model(object_model, 'object-detection')
|
||||
assetPath = request.json['thumbnailPath']
|
||||
return run_engine(model, assetPath), 200
|
||||
|
||||
@server.route("/image-classifier/tag-image", methods=['POST'])
|
||||
def image_classification():
|
||||
model = _get_model(classification_model, 'image-classification')
|
||||
assetPath = request.json['thumbnailPath']
|
||||
return run_engine(model, assetPath), 200
|
||||
|
||||
@server.route("/sentence-transformer/encode-image", methods=['POST'])
|
||||
def clip_encode_image():
|
||||
model = _get_model(clip_image_model)
|
||||
assetPath = request.json['thumbnailPath']
|
||||
return model.encode(Image.open(assetPath)).tolist(), 200
|
||||
|
||||
@server.route("/sentence-transformer/encode-text", methods=['POST'])
|
||||
def clip_encode_text():
|
||||
model = _get_model(clip_text_model)
|
||||
text = request.json['text']
|
||||
return model.encode(text).tolist(), 200
|
||||
|
||||
def run_engine(engine, path):
|
||||
result = []
|
||||
predictions = engine(path)
|
||||
|
||||
for index, pred in enumerate(predictions):
|
||||
tags = pred['label'].split(', ')
|
||||
if (pred['score'] > 0.9):
|
||||
result = [*result, *tags]
|
||||
|
||||
if (len(result) > 1):
|
||||
result = list(set(result))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
server.run(debug=is_dev, host=server_host, port=server_port)
|
||||
@@ -1,27 +0,0 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
const port = Number(process.env.MACHINE_LEARNING_PORT) || 3003;
|
||||
|
||||
await app.listen(port, () => {
|
||||
if (process.env.NODE_ENV == 'development') {
|
||||
Logger.log(
|
||||
'Running Immich Machine Learning in DEVELOPMENT environment',
|
||||
'IMMICH MICROSERVICES',
|
||||
);
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV == 'production') {
|
||||
Logger.log(
|
||||
'Running Immich Machine Learning in PRODUCTION environment',
|
||||
'IMMICH MICROSERVICES',
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
@@ -1,15 +0,0 @@
|
||||
import { Body, Controller, Post } from '@nestjs/common';
|
||||
import { ObjectDetectionService } from './object-detection.service';
|
||||
import { Logger } from '@nestjs/common';
|
||||
|
||||
@Controller('object-detection')
|
||||
export class ObjectDetectionController {
|
||||
constructor(
|
||||
private readonly objectDetectionService: ObjectDetectionService,
|
||||
) { }
|
||||
|
||||
@Post('/detect-object')
|
||||
async detectObject(@Body('thumbnailPath') thumbnailPath: string) {
|
||||
return await this.objectDetectionService.detectObject(thumbnailPath);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ObjectDetectionService } from './object-detection.service';
|
||||
import { ObjectDetectionController } from './object-detection.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [ObjectDetectionController],
|
||||
providers: [ObjectDetectionService],
|
||||
})
|
||||
export class ObjectDetectionModule {}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import * as cocoSsd from '@tensorflow-models/coco-ssd';
|
||||
import * as tf from '@tensorflow/tfjs-node';
|
||||
import * as fs from 'fs';
|
||||
|
||||
@Injectable()
|
||||
export class ObjectDetectionService {
|
||||
private cocoSsdModel: cocoSsd.ObjectDetection;
|
||||
|
||||
constructor() {
|
||||
Logger.log(
|
||||
`Running Node TensorFlow Version : ${tf.version['tfjs']}`,
|
||||
'ObjectDetection',
|
||||
);
|
||||
cocoSsd.load().then((model) => (this.cocoSsdModel = model));
|
||||
}
|
||||
async detectObject(thumbnailPath: string) {
|
||||
try {
|
||||
const isExist = fs.existsSync(thumbnailPath);
|
||||
if (isExist) {
|
||||
const tags = new Set();
|
||||
const image = fs.readFileSync(thumbnailPath);
|
||||
const decodedImage = tf.node.decodeImage(image, 3) as tf.Tensor3D;
|
||||
const predictions = await this.cocoSsdModel.detect(decodedImage);
|
||||
|
||||
for (const result of predictions) {
|
||||
if (result.score > 0.5) {
|
||||
tags.add(result.class);
|
||||
}
|
||||
}
|
||||
|
||||
tf.dispose(decodedImage);
|
||||
return [...tags];
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Error reading file ', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "es2017",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"strictNullChecks": false,
|
||||
"noImplicitAny": false,
|
||||
"strictBindCallApply": false,
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"noFallthroughCasesInSwitch": false
|
||||
}
|
||||
}
|
||||
@@ -60,22 +60,27 @@ else
|
||||
fi
|
||||
|
||||
if [ "$CURRENT_SERVER" != "$NEXT_SERVER" ]; then
|
||||
|
||||
echo "Pumping Server: $CURRENT_SERVER => $NEXT_SERVER"
|
||||
|
||||
sed -i "s/^ \"version\": \"$CURRENT_SERVER\",$/ \"version\": \"$NEXT_SERVER\",/" server/package.json
|
||||
sed -i "s/^ \"version\": \"$CURRENT_SERVER\",$/ \"version\": \"$NEXT_SERVER\",/" server/package-lock.json
|
||||
sed -i "s/\"version\": \"$CURRENT_SERVER\",$/\"version\": \"$NEXT_SERVER\",/" server/immich-openapi-specs.json
|
||||
sed -i "s/\"android\.injected\.version\.name\" => \"$CURRENT_SERVER\",/\"android\.injected\.version\.name\" => \"$NEXT_SERVER\",/" mobile/android/fastlane/Fastfile
|
||||
sed -i "s/version_number: \"$CURRENT_SERVER\"$/version_number: \"$NEXT_SERVER\"/" mobile/ios/fastlane/Fastfile
|
||||
fi
|
||||
|
||||
if [ "$CURRENT_MOBILE" != "$NEXT_MOBILE" ]; then
|
||||
|
||||
echo "Pumping Mobile: $CURRENT_MOBILE => $NEXT_MOBILE"
|
||||
|
||||
sed -i "s/\"android\.injected\.version\.code\" => $CURRENT_MOBILE,/\"android\.injected\.version\.code\" => $NEXT_MOBILE,/" mobile/android/fastlane/Fastfile
|
||||
sed -i "s/^version: $CURRENT_SERVER+$CURRENT_MOBILE$/version: $NEXT_SERVER+$NEXT_MOBILE/" mobile/pubspec.yaml
|
||||
fi
|
||||
|
||||
sed -i "s/^ \"version\": \"$CURRENT_SERVER\",$/ \"version\": \"$NEXT_SERVER\",/" server/package.json
|
||||
sed -i "s/^ \"version\": \"$CURRENT_SERVER\",$/ \"version\": \"$NEXT_SERVER\",/" server/package-lock.json
|
||||
sed -i "s/\"version\": \"$CURRENT_SERVER\",$/\"version\": \"$NEXT_SERVER\",/" server/immich-openapi-specs.json
|
||||
sed -i "s/\"android\.injected\.version\.name\" => \"$CURRENT_SERVER\",/\"android\.injected\.version\.name\" => \"$NEXT_SERVER\",/" mobile/android/fastlane/Fastfile
|
||||
sed -i "s/version_number: \"$CURRENT_SERVER\"$/version_number: \"$NEXT_SERVER\"/" mobile/ios/fastlane/Fastfile
|
||||
sed -i "s/\"android\.injected\.version\.code\" => $CURRENT_MOBILE,/\"android\.injected\.version\.code\" => $NEXT_MOBILE,/" mobile/android/fastlane/Fastfile
|
||||
sed -i "s/^version: $CURRENT_SERVER+$CURRENT_MOBILE$/version: $NEXT_SERVER+$NEXT_MOBILE/" mobile/pubspec.yaml
|
||||
|
||||
# OpenApi Generated Files
|
||||
sed -i "s/API version: $CURRENT_SERVER,$/API version: $NEXT_SERVER/" mobile/openapi/README.md
|
||||
sed -i "s/OpenAPI document: $CURRENT_SERVER,$/OpenAPI document: $NEXT_SERVER/" web/src/api/open-api/api.ts
|
||||
sed -i "s/OpenAPI document: $CURRENT_SERVER,$/OpenAPI document: $NEXT_SERVER/" web/src/api/open-api/base.ts
|
||||
sed -i "s/OpenAPI document: $CURRENT_SERVER,$/OpenAPI document: $NEXT_SERVER/" web/src/api/open-api/common.ts
|
||||
sed -i "s/OpenAPI document: $CURRENT_SERVER,$/OpenAPI document: $NEXT_SERVER/" web/src/api/open-api/configuration.ts
|
||||
sed -i "s/OpenAPI document: $CURRENT_SERVER,$/OpenAPI document: $NEXT_SERVER/" web/src/api/open-api/index.ts
|
||||
|
||||
echo "IMMICH_VERSION=v$NEXT_SERVER" >>$GITHUB_ENV
|
||||
|
||||
4
mobile/.fvm/fvm_config.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"flutterSdkVersion": "3.7.0",
|
||||
"flavors": {}
|
||||
}
|
||||
4
mobile/.gitignore
vendored
@@ -24,7 +24,6 @@
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
**/doc/api/
|
||||
**/ios/
|
||||
.dart_tool/
|
||||
.flutter-plugins
|
||||
.flutter-plugins-dependencies
|
||||
@@ -32,6 +31,7 @@
|
||||
.pub-cache/
|
||||
.pub/
|
||||
/build/
|
||||
.fvm/flutter_sdk
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
@@ -48,4 +48,4 @@ app.*.map.json
|
||||
/android/app/release
|
||||
|
||||
# Fastlane
|
||||
ios/fastlane/report.xml
|
||||
ios/fastlane/report.xml
|
||||
|
||||
1
mobile/.isar
Submodule
@@ -35,8 +35,8 @@ platform :android do
|
||||
task: 'bundle',
|
||||
build_type: 'Release',
|
||||
properties: {
|
||||
"android.injected.version.code" => 70,
|
||||
"android.injected.version.name" => "1.47.2",
|
||||
"android.injected.version.code" => 74,
|
||||
"android.injected.version.name" => "1.51.1",
|
||||
}
|
||||
)
|
||||
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')
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
* Adds over scroll at end of timeline to select images on the bottom
|
||||
* Fixes back navigation with tab controller
|
||||
* Shows a toast after adding to favorites
|
||||
* Fixed download button style
|
||||
* Cleaned up action bar, changed horizontal icon more to info icon
|
||||
* Responsive display of exif data in bottom sheet
|
||||
* Upgrade to Flutter 3.7
|
||||
* Fix freeze bug on app start
|
||||
* Uses profile photo for user avatar drawer
|
||||
* Spinning flower
|
||||
* Remove unsplash placeholder image and style empty places
|
||||
@@ -0,0 +1,6 @@
|
||||
* Scroll to top when tapping photos while already on photo page.
|
||||
* Delete goes to next page instead of popping back to the main timeline.
|
||||
* Improve date formatting.
|
||||
* Styling and linter.
|
||||
* User get logged out upon clicking on any thing after logging in.
|
||||
* Improve reliability of asset loading and indexing.
|
||||
@@ -0,0 +1,2 @@
|
||||
* Fix no album thumbnail lead to no album selection shown and add global logs
|
||||
* Fix remove asset from gallery view
|
||||
@@ -0,0 +1,4 @@
|
||||
* fix: Prevents duplicate taps navigating to the same route twice.
|
||||
* fix: Adds safe area to album to stop from clipping bottom of albums.
|
||||
* feat: Adds onboarding for permissions.
|
||||
* feat: Responsive list and grid view of backup album selection and fixes search filter.
|
||||
@@ -0,0 +1,12 @@
|
||||
* Enter server first for login
|
||||
* Sync assets, albums & users to local database on device
|
||||
* Fixes hero animation on main timeline by
|
||||
* Transparent bottom Android navigation bar
|
||||
* Fix do not crash on malformed asset duration
|
||||
* Gallery viewer fullscreen edge case
|
||||
* Fix Sorted shared album and added share user doesn't reflect change in album view
|
||||
* Allow app to be used offline
|
||||
* No longer wait for background backup in settings
|
||||
* Share album name and adaptive shared album display
|
||||
* Persist album sort order
|
||||
|
||||
@@ -5,17 +5,19 @@
|
||||
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000281">
|
||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000232">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="1: bundleRelease" time="142.850758">
|
||||
<testcase classname="fastlane.lanes" name="1: bundleRelease" time="70.685298">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="39.589103">
|
||||
<testcase classname="fastlane.lanes" name="2: upload_to_play_store" time="33.624781">
|
||||
|
||||
<failure message="/opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/actions/actions_helper.rb:67:in `execute_action' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/runner.rb:255:in `block in execute_action' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/runner.rb:229:in `chdir' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/runner.rb:229:in `execute_action' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/runner.rb:157:in `trigger_action_by_name' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/fast_file.rb:159:in `method_missing' Fastfile:42:in `block (2 levels) in parsing_binding' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/lane.rb:33:in `call' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/runner.rb:49:in `block in execute' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/runner.rb:45:in `chdir' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/runner.rb:45:in `execute' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/lane_manager.rb:47:in `cruise_lane' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/command_line_handler.rb:36:in `handle' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/commands_generator.rb:110:in `block (2 levels) in run' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/commander-4.6.0/lib/commander/command.rb:187:in `call' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/commander-4.6.0/lib/commander/command.rb:157:in `run' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/commander-4.6.0/lib/commander/runner.rb:444:in `run_active_command' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb:124:in `run!' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/commander-4.6.0/lib/commander/delegates.rb:18:in `run!' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/commands_generator.rb:354:in `run' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/commands_generator.rb:43:in `start' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/fastlane/lib/fastlane/cli_tools_distributor.rb:123:in `take_off' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/gems/fastlane-2.212.0/bin/fastlane:23:in `<top (required)>' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/bin/fastlane:25:in `load' /opt/homebrew/Cellar/fastlane/2.212.0/libexec/bin/fastlane:25:in `<main>' Google Api Error: Invalid request - The release created has notes in language en-US with length 508, which is too long (max: 500)." />
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Přidáno do {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Již v {album}",
|
||||
"album_info_card_backup_album_excluded": "VYLOUČENO",
|
||||
"album_info_card_backup_album_included": "ZAHRNUTO",
|
||||
"album_thumbnail_card_item": "1 položka",
|
||||
@@ -12,10 +14,10 @@
|
||||
"album_viewer_appbar_share_leave": "Opustit album",
|
||||
"album_viewer_appbar_share_remove": "Odstranit z alba",
|
||||
"album_viewer_page_share_add_users": "Přidat uživatele",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamic layout",
|
||||
"asset_list_layout_settings_group_by": "Group assets by",
|
||||
"asset_list_layout_settings_group_by_month": "Month",
|
||||
"asset_list_layout_settings_group_by_month_day": "Month + day",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamické rozložení",
|
||||
"asset_list_layout_settings_group_by": "Seskupit položky podle",
|
||||
"asset_list_layout_settings_group_by_month": "Měsíc",
|
||||
"asset_list_layout_settings_group_by_month_day": "Měsíc + den",
|
||||
"asset_list_settings_subtitle": "Nastavení rozložení mřížky fotografií",
|
||||
"asset_list_settings_title": "Fotografická mřížka",
|
||||
"backup_album_selection_page_albums_device": "Alba v zařízení ({})",
|
||||
@@ -25,21 +27,24 @@
|
||||
"backup_album_selection_page_selection_info": "Informace o výběru",
|
||||
"backup_album_selection_page_total_assets": "Celkový počet jedinečných souborů",
|
||||
"backup_all": "Vše",
|
||||
"backup_background_service_backup_failed_message": "Zálohování zdrojů selhalo. Zkouším to znovu...",
|
||||
"backup_background_service_backup_failed_message": "Zálohování médií selhalo. Zkouším to znovu...",
|
||||
"backup_background_service_connection_failed_message": "Nepodařilo se připojit k serveru. Zkouším to znovu...",
|
||||
"backup_background_service_current_upload_notification": "Nahrávání {}",
|
||||
"backup_background_service_default_notification": "Kontrola nových zdrojů {}",
|
||||
"backup_background_service_default_notification": "Kontrola nových médií {}",
|
||||
"backup_background_service_error_title": "Chyba zálohování",
|
||||
"backup_background_service_in_progress_notification": "Vytvářím kopii vašich zdrojů...",
|
||||
"backup_background_service_in_progress_notification": "Vytvářím kopii vašich médií...",
|
||||
"backup_background_service_upload_failure_notification": "Nepodařilo se nahrát {}",
|
||||
"backup_controller_page_albums": "Zálohovaná alba",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "Ukaž mi jak",
|
||||
"backup_controller_page_background_battery_info_message": "Chcete-li dosáhnout nejlepších výsledků při zálohování na pozadí, vypněte všechny optimalizace baterie, které omezují aktivitu na pozadí pro Immich ve vašem zařízení. Jelikož to závisí na zařízení, zkontrolujte požadované informace pro výrobce vašeho zařízení.",
|
||||
"backup_controller_page_background_battery_info_ok": "OK",
|
||||
"backup_controller_page_background_battery_info_title": "Optimalizace baterie",
|
||||
"backup_controller_page_background_charging": "Pouze během nabíjení",
|
||||
"backup_controller_page_background_configure_error": "Nepodařilo se nakonfigurovat službu na pozadí",
|
||||
"backup_controller_page_background_delay": "Zpoždění zálohování nových zdrojů: {}",
|
||||
"backup_controller_page_background_delay": "Zpoždění zálohování nových médií: {}",
|
||||
"backup_controller_page_background_description": "Povolte službu na pozadí pro automatické zálohování všech nových aktiv bez nutnosti otevření aplikace",
|
||||
"backup_controller_page_background_is_off": "Automatické zálohování na pozadí je vypnuto",
|
||||
"backup_controller_page_background_is_on": "Automatické zálohování na pozadí je zapnuto",
|
||||
@@ -87,11 +92,21 @@
|
||||
"cache_settings_subtitle": "Ovládání chování mobilní aplikace Immich v mezipaměti",
|
||||
"cache_settings_thumbnail_size": "Velikost vyrovnávací paměti náhledů (položek {})",
|
||||
"cache_settings_title": "Nastavení vyrovnávací paměti",
|
||||
"change_password_form_confirm_password": "Potvrďte heslo",
|
||||
"change_password_form_description": "Dobrý den, {firstName} {lastName},\n\nJe to buď poprvé, co se přihlašujete do systému, nebo byl podán požadavek na změnu hesla. Níže prosím zadejte nové heslo.",
|
||||
"change_password_form_new_password": "Nové heslo",
|
||||
"change_password_form_password_mismatch": "Hesla se neshodují",
|
||||
"change_password_form_reenter_new_password": "Znovu zadejte nové heslo",
|
||||
"common_add_to_album": "Přidat do alba",
|
||||
"common_change_password": "Změnit heslo",
|
||||
"common_create_new_album": "Vytvořit nové album",
|
||||
"common_shared": "Sdílené",
|
||||
"control_bottom_app_bar_add_to_album": "Přidat do alba",
|
||||
"control_bottom_app_bar_album_info": "{} položky",
|
||||
"control_bottom_app_bar_album_info_shared": "{} položky - sdílené",
|
||||
"control_bottom_app_bar_create_new_album": "Vytvořit nové album",
|
||||
"control_bottom_app_bar_delete": "Vymazat",
|
||||
"control_bottom_app_bar_favorite": "Oblíbené",
|
||||
"control_bottom_app_bar_share": "Sdílet",
|
||||
"create_album_page_untitled": "Bez názvu",
|
||||
"create_shared_album_page_create": "Vytvořit",
|
||||
@@ -114,9 +129,13 @@
|
||||
"experimental_settings_title": "Experimentální",
|
||||
"favorites_page_title": "Oblíbené",
|
||||
"home_page_add_to_album_conflicts": "Přidány {added} položky do alba {album}. {failed} položky jsou již v albu.",
|
||||
"home_page_add_to_album_err_local": "Zatím není možné přidat lokální média do alb, přeskakuje se",
|
||||
"home_page_add_to_album_success": "Přidány položky {added} do alba {album}.",
|
||||
"home_page_building_timeline": "Vytváraní časové osy",
|
||||
"home_page_favorite_err_local": "Zatím není možné zařadit lokální média mezi oblíbená, přeskakuje se",
|
||||
"home_page_first_time_notice": "Pokud aplikaci používáte poprvé, nezapomeňte si vybrat zálohovaná alba, aby se na časové ose mohly nacházet fotografie a videa z vybraných albech.",
|
||||
"image_viewer_page_state_provider_download_error": "Chyba stahování",
|
||||
"image_viewer_page_state_provider_download_success": "Stahování bylo úspěšné",
|
||||
"library_page_albums": "Alba",
|
||||
"library_page_favorites": "Oblíbené",
|
||||
"library_page_new_album": "Nové album",
|
||||
@@ -140,6 +159,12 @@
|
||||
"login_form_password_hint": "heslo",
|
||||
"login_form_save_login": "Zůstat přihlášen",
|
||||
"monthly_title_text_date_format": "LLLL y",
|
||||
"notification_permission_dialog_cancel": "Zrušit",
|
||||
"notification_permission_dialog_content": "Chcete-li povolit oznámení, přejděte do Nastavení a vyberte možnost Povolit.",
|
||||
"notification_permission_dialog_settings": "Nastavení",
|
||||
"notification_permission_list_tile_content": "Udělte oprávnění k aktivaci oznámení.",
|
||||
"notification_permission_list_tile_enable_button": "Povolit oznámení",
|
||||
"notification_permission_list_tile_title": "Povolení oznámení",
|
||||
"profile_drawer_app_logs": "Logy",
|
||||
"profile_drawer_client_server_up_to_date": "Klient a server jsou aktuální",
|
||||
"profile_drawer_settings": "Nastavení",
|
||||
@@ -153,6 +178,8 @@
|
||||
"select_additional_user_for_sharing_page_suggestions": "Návrhy",
|
||||
"select_user_for_sharing_page_err_album": "Nepodařilo se vytvořit album",
|
||||
"select_user_for_sharing_page_share_suggestions": "Návrhy",
|
||||
"server_info_box_app_version": "Verze aplikace",
|
||||
"server_info_box_server_version": "Verze serveru",
|
||||
"setting_image_viewer_help": "V prohlížeči detailů se nejprve načte malá miniatura, poté se načte náhled střední velikosti (je-li povolen) a nakonec se načte originál (je-li povolen).",
|
||||
"setting_image_viewer_original_subtitle": "Umožňuje načíst původní obrázek v plném rozlišení (velký!). Zakázat pro snížení používání dat (v síti iv mezipaměti zařízení).",
|
||||
"setting_image_viewer_original_title": "Načíst původní obrázek",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
||||
"album_info_card_backup_album_excluded": "EKSKLUDERET",
|
||||
"album_info_card_backup_album_included": "INKLUDERET",
|
||||
"album_thumbnail_card_item": "1 genstand",
|
||||
@@ -12,10 +14,10 @@
|
||||
"album_viewer_appbar_share_leave": "Forlad album",
|
||||
"album_viewer_appbar_share_remove": "Fjern fra album",
|
||||
"album_viewer_page_share_add_users": "Tilføj brugere",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamic layout",
|
||||
"asset_list_layout_settings_group_by": "Group assets by",
|
||||
"asset_list_layout_settings_group_by_month": "Month",
|
||||
"asset_list_layout_settings_group_by_month_day": "Month + day",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamisk layout",
|
||||
"asset_list_layout_settings_group_by": "Gruppér elementer pr. ",
|
||||
"asset_list_layout_settings_group_by_month": "Måned",
|
||||
"asset_list_layout_settings_group_by_month_day": "Måned + dag",
|
||||
"asset_list_settings_subtitle": "Indstillinger for billedgitterlayout",
|
||||
"asset_list_settings_title": "Billedgitter",
|
||||
"backup_album_selection_page_albums_device": "Albummer på enhed ({})",
|
||||
@@ -25,22 +27,25 @@
|
||||
"backup_album_selection_page_selection_info": "Oplysninger om valgte",
|
||||
"backup_album_selection_page_total_assets": "Samlede unikke elementer",
|
||||
"backup_all": "Alt",
|
||||
"backup_background_service_backup_failed_message": "Backup af billeder og videoer fejlede. Forsøger igen...",
|
||||
"backup_background_service_backup_failed_message": "Backup af elementer fejlede. Forsøger igen...",
|
||||
"backup_background_service_connection_failed_message": "Forbindelsen til serveren blev tabt. Forsøger igen...",
|
||||
"backup_background_service_current_upload_notification": "Uploader {}",
|
||||
"backup_background_service_default_notification": "Checking for new assets…",
|
||||
"backup_background_service_default_notification": "Søger efter nye elementer...",
|
||||
"backup_background_service_error_title": "Fejl med backup",
|
||||
"backup_background_service_in_progress_notification": "Tager backup af dine billeder og videoer...",
|
||||
"backup_background_service_in_progress_notification": "Tager backup af dine elementer...",
|
||||
"backup_background_service_upload_failure_notification": "Failed to upload {}",
|
||||
"backup_controller_page_albums": "Sikkerhedskopiér albummer",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "Vis mig hvordan",
|
||||
"backup_controller_page_background_battery_info_message": "For den bedste oplevelse med baggrundsbackup, bør du slå batterioptimering, der begrænder baggrundsaktivitet, fra.\n\nSiden dette er afhængigt af enheden, bør du undersøge denne information leveret af din enheds producent.",
|
||||
"backup_controller_page_background_battery_info_ok": "OK",
|
||||
"backup_controller_page_background_battery_info_title": "Batterioptimering",
|
||||
"backup_controller_page_background_charging": "Kun under opladning",
|
||||
"backup_controller_page_background_configure_error": "Fejlede konfigureringen af baggrundsbackup",
|
||||
"backup_controller_page_background_delay": "Udskyd backup af nye billeder og videoer: {}",
|
||||
"backup_controller_page_background_description": "Slå baggrundsbackup til, for automatisk at tage backup af nye billeder og videoer, uden at skulle åbne appen",
|
||||
"backup_controller_page_background_delay": "Udskyd backup af nye elementer: {}",
|
||||
"backup_controller_page_background_description": "Slå baggrundsbackup til, for automatisk at tage backup af nye elementer, uden at skulle åbne appen",
|
||||
"backup_controller_page_background_is_off": "Automatisk baggrundsbackup er slået fra",
|
||||
"backup_controller_page_background_is_on": "Automatisk baggrundsbackup er slået til",
|
||||
"backup_controller_page_background_turn_off": "Slå baggrundsbackup fra",
|
||||
@@ -74,24 +79,34 @@
|
||||
"backup_controller_page_uploading_file_info": "Uploader filinformation",
|
||||
"backup_err_only_album": "Kan ikke slette det eneste album",
|
||||
"backup_info_card_assets": "elementer",
|
||||
"cache_settings_album_thumbnails": "Biblioteksminiaturebilleder ({} billeder og videoer)",
|
||||
"cache_settings_album_thumbnails": "Biblioteksminiaturebilleder ({} elementer)",
|
||||
"cache_settings_clear_cache_button": "Fjern cache",
|
||||
"cache_settings_clear_cache_button_title": "Fjern appens cache. Dette vil i stor grad påvirke appens ydeevne indtil cachen er genopbygget.",
|
||||
"cache_settings_image_cache_size": "Størrelse af billedecache ({} billeder og videoer)",
|
||||
"cache_settings_image_cache_size": "Størrelse af billedecache ({} elementer)",
|
||||
"cache_settings_statistics_album": "Biblioteksminiaturer",
|
||||
"cache_settings_statistics_assets": "{} billeder og videoer ({})",
|
||||
"cache_settings_statistics_assets": "{} elementer ({})",
|
||||
"cache_settings_statistics_full": "Fulde billeder",
|
||||
"cache_settings_statistics_shared": "Miniaturebilleder til delte albummer",
|
||||
"cache_settings_statistics_thumbnail": "Miniaturebilleder",
|
||||
"cache_settings_statistics_title": "Cacheforbrug",
|
||||
"cache_settings_subtitle": "Håndter cache-adfærden for Immich-appen.",
|
||||
"cache_settings_thumbnail_size": "Størrelse af miniaturebillede cache ({} billeder og videoer)",
|
||||
"cache_settings_thumbnail_size": "Størrelse af miniaturebillede cache ({} elementer)",
|
||||
"cache_settings_title": "Cache-indstillinger",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
"common_add_to_album": "Add to album",
|
||||
"common_change_password": "Change Password",
|
||||
"common_create_new_album": "Create new album",
|
||||
"common_shared": "Shared",
|
||||
"control_bottom_app_bar_add_to_album": "Tilføj til album",
|
||||
"control_bottom_app_bar_album_info": "{} genstande",
|
||||
"control_bottom_app_bar_album_info_shared": "{} genstande • Delt",
|
||||
"control_bottom_app_bar_create_new_album": "Opret nyt album",
|
||||
"control_bottom_app_bar_delete": "Slet",
|
||||
"control_bottom_app_bar_favorite": "Favorite",
|
||||
"control_bottom_app_bar_share": "Del",
|
||||
"create_album_page_untitled": "Uden titel",
|
||||
"create_shared_album_page_create": "Opret",
|
||||
@@ -113,10 +128,14 @@
|
||||
"experimental_settings_subtitle": "Brug på eget ansvar!",
|
||||
"experimental_settings_title": "Eksperimentelle",
|
||||
"favorites_page_title": "Favoritter",
|
||||
"home_page_add_to_album_conflicts": "Tilføjede {added} billeder og videoer til album {album}. {failed} billeder og videoer er allerede i albummet.",
|
||||
"home_page_add_to_album_success": "Tilføjede {added} billeder og videoer til album {album}.",
|
||||
"home_page_add_to_album_conflicts": "Tilføjede {added} elementer til album {album}. {failed} elementer er allerede i albummet.",
|
||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
||||
"home_page_add_to_album_success": "Tilføjede {added} elementer til album {album}.",
|
||||
"home_page_building_timeline": "Bygger tidslinjen",
|
||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
||||
"home_page_first_time_notice": "Hvis dette er din første gang i appen, bedes du vælge en backup af albummer så tidlinjen kan blive fyldt med billeder og videoer fra albummerne.",
|
||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
||||
"library_page_albums": "Albummer",
|
||||
"library_page_favorites": "Favoritter",
|
||||
"library_page_new_album": "Nyt album",
|
||||
@@ -140,6 +159,12 @@
|
||||
"login_form_password_hint": "kodeord",
|
||||
"login_form_save_login": "Forbliv logget ind",
|
||||
"monthly_title_text_date_format": "MMMM y",
|
||||
"notification_permission_dialog_cancel": "Cancel",
|
||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
||||
"notification_permission_dialog_settings": "Settings",
|
||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
||||
"notification_permission_list_tile_title": "Notification Permission",
|
||||
"profile_drawer_app_logs": "Log",
|
||||
"profile_drawer_client_server_up_to_date": "Klient og server er ajour",
|
||||
"profile_drawer_settings": "Indstillinger",
|
||||
@@ -153,6 +178,8 @@
|
||||
"select_additional_user_for_sharing_page_suggestions": "Anbefalinger",
|
||||
"select_user_for_sharing_page_err_album": "Fejlede i at oprette et nyt album",
|
||||
"select_user_for_sharing_page_share_suggestions": "Anbefalinger",
|
||||
"server_info_box_app_version": "App Version",
|
||||
"server_info_box_server_version": "Server Version",
|
||||
"setting_image_viewer_help": "Detaljeret visning indlæser miniaturebilleder først. Herefter indlæses mediumstørrelse forhåndsvisning af billedet (hvis dette er slået til), for til sidst at vise originalen (hvis dette er slået til).",
|
||||
"setting_image_viewer_original_subtitle": "Slå indlæsning af originalbillede i fuld størrelse til (stort!). Deaktiver for at reducere dataforbruget (både på netværket og for enhedscache).",
|
||||
"setting_image_viewer_original_title": "Indlæs originalbillede",
|
||||
@@ -164,11 +191,11 @@
|
||||
"setting_notifications_notify_minutes": "{} minutter",
|
||||
"setting_notifications_notify_never": "aldrig",
|
||||
"setting_notifications_notify_seconds": "{} sekunder",
|
||||
"setting_notifications_single_progress_subtitle": "Detaljeret uploadstatus pr. billed og video",
|
||||
"setting_notifications_single_progress_subtitle": "Detaljeret uploadstatus pr. element",
|
||||
"setting_notifications_single_progress_title": "Vis detaljeret baggrundsuploadstatus",
|
||||
"setting_notifications_subtitle": "Tilpas dine notifikationspræferencer",
|
||||
"setting_notifications_title": "Notifikationer",
|
||||
"setting_notifications_total_progress_subtitle": "Samlet uploadstatus (færdige/samlede billeder og videoer)",
|
||||
"setting_notifications_total_progress_subtitle": "Samlet uploadstatus (færdige/samlet antal elementer)",
|
||||
"setting_notifications_total_progress_title": "Vis samlet baggrundsuploadstatus",
|
||||
"setting_pages_app_bar_settings": "Indstillinger",
|
||||
"settings_require_restart": "Genstart venligst Immich for at anvende denne ændring",
|
||||
@@ -188,7 +215,7 @@
|
||||
"tab_controller_nav_search": "Søg",
|
||||
"tab_controller_nav_sharing": "Deling",
|
||||
"theme_setting_asset_list_storage_indicator_title": "Vis opbevaringsindikator på filer",
|
||||
"theme_setting_asset_list_tiles_per_row_title": "Antal billeder og videoer per række ({})",
|
||||
"theme_setting_asset_list_tiles_per_row_title": "Antal elementer per række ({})",
|
||||
"theme_setting_dark_mode_switch": "Mørk tilstand",
|
||||
"theme_setting_image_viewer_quality_subtitle": "Juster kvaliteten i billedfremviseren",
|
||||
"theme_setting_image_viewer_quality_title": "Billedfremviserkvalitet",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
||||
"album_info_card_backup_album_excluded": "AUSGESCHLOSSEN",
|
||||
"album_info_card_backup_album_included": "EINGESCHLOSSEN",
|
||||
"album_thumbnail_card_item": "1 Element",
|
||||
@@ -33,6 +35,9 @@
|
||||
"backup_background_service_in_progress_notification": "Backing up your assets…",
|
||||
"backup_background_service_upload_failure_notification": "Failed to upload {}",
|
||||
"backup_controller_page_albums": "Gesicherte Alben",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "Show me how",
|
||||
"backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.",
|
||||
"backup_controller_page_background_battery_info_ok": "OK",
|
||||
@@ -87,11 +92,21 @@
|
||||
"cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application",
|
||||
"cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)",
|
||||
"cache_settings_title": "Caching Settings",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
"common_add_to_album": "Add to album",
|
||||
"common_change_password": "Change Password",
|
||||
"common_create_new_album": "Create new album",
|
||||
"common_shared": "Shared",
|
||||
"control_bottom_app_bar_add_to_album": "Add to album",
|
||||
"control_bottom_app_bar_album_info": "{} items",
|
||||
"control_bottom_app_bar_album_info_shared": "{} items · Shared",
|
||||
"control_bottom_app_bar_create_new_album": "Neues Album erstellen",
|
||||
"control_bottom_app_bar_delete": "Löschen",
|
||||
"control_bottom_app_bar_favorite": "Favorite",
|
||||
"control_bottom_app_bar_share": "Teilen",
|
||||
"create_album_page_untitled": "Unbenannt",
|
||||
"create_shared_album_page_create": "Erstellen",
|
||||
@@ -114,9 +129,13 @@
|
||||
"experimental_settings_title": "Experimentell",
|
||||
"favorites_page_title": "Favorites",
|
||||
"home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.",
|
||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
||||
"home_page_add_to_album_success": "Added {added} assets to album {album}.",
|
||||
"home_page_building_timeline": "Building the timeline",
|
||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
||||
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
|
||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
||||
"library_page_albums": "Alben",
|
||||
"library_page_favorites": "Favorites",
|
||||
"library_page_new_album": "Neues Album",
|
||||
@@ -140,6 +159,12 @@
|
||||
"login_form_password_hint": "password",
|
||||
"login_form_save_login": "Angemeldet bleiben",
|
||||
"monthly_title_text_date_format": "MMMM y",
|
||||
"notification_permission_dialog_cancel": "Cancel",
|
||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
||||
"notification_permission_dialog_settings": "Settings",
|
||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
||||
"notification_permission_list_tile_title": "Notification Permission",
|
||||
"profile_drawer_app_logs": "Logs",
|
||||
"profile_drawer_client_server_up_to_date": "App und Server sind aktuell",
|
||||
"profile_drawer_settings": "Einstellungen",
|
||||
@@ -153,6 +178,8 @@
|
||||
"select_additional_user_for_sharing_page_suggestions": "Vorschläge",
|
||||
"select_user_for_sharing_page_err_album": "Album konnte nicht erstellt werden",
|
||||
"select_user_for_sharing_page_share_suggestions": "Suggestions",
|
||||
"server_info_box_app_version": "App Version",
|
||||
"server_info_box_server_version": "Server Version",
|
||||
"setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).",
|
||||
"setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).",
|
||||
"setting_image_viewer_original_title": "Load original image",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
||||
"album_info_card_backup_album_excluded": "EXCLUDED",
|
||||
"album_info_card_backup_album_included": "INCLUDED",
|
||||
"album_thumbnail_card_item": "1 item",
|
||||
@@ -33,6 +35,9 @@
|
||||
"backup_background_service_in_progress_notification": "Backing up your assets…",
|
||||
"backup_background_service_upload_failure_notification": "Failed to upload {}",
|
||||
"backup_controller_page_albums": "Backup Albums",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "Show me how",
|
||||
"backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.",
|
||||
"backup_controller_page_background_battery_info_ok": "OK",
|
||||
@@ -87,11 +92,22 @@
|
||||
"cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application",
|
||||
"cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)",
|
||||
"cache_settings_title": "Caching Settings",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
"common_add_to_album": "Add to album",
|
||||
"common_change_password": "Change Password",
|
||||
"common_create_new_album": "Create new album",
|
||||
"common_shared": "Shared",
|
||||
"common_server_error": "Please check your network connection, make sure the server is reachable and app/server versions are compatible.",
|
||||
"control_bottom_app_bar_add_to_album": "Add to album",
|
||||
"control_bottom_app_bar_album_info": "{} items",
|
||||
"control_bottom_app_bar_album_info_shared": "{} items · Shared",
|
||||
"control_bottom_app_bar_create_new_album": "Create new album",
|
||||
"control_bottom_app_bar_delete": "Delete",
|
||||
"control_bottom_app_bar_favorite": "Favorite",
|
||||
"control_bottom_app_bar_share": "Share",
|
||||
"create_album_page_untitled": "Untitled",
|
||||
"create_shared_album_page_create": "Create",
|
||||
@@ -114,15 +130,20 @@
|
||||
"experimental_settings_title": "Experimental",
|
||||
"favorites_page_title": "Favorites",
|
||||
"home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.",
|
||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
||||
"home_page_add_to_album_success": "Added {added} assets to album {album}.",
|
||||
"home_page_building_timeline": "Building the timeline",
|
||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
||||
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
|
||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
||||
"library_page_albums": "Albums",
|
||||
"library_page_favorites": "Favorites",
|
||||
"library_page_new_album": "New album",
|
||||
"library_page_sharing": "Sharing",
|
||||
"library_page_sort_created": "Most recently created",
|
||||
"library_page_sort_title": "Album title",
|
||||
"library_page_device_albums": "Albums on Device",
|
||||
"login_form_button_text": "Login",
|
||||
"login_form_email_hint": "youremail@email.com",
|
||||
"login_form_endpoint_hint": "http://your-server-ip:port/api",
|
||||
@@ -137,9 +158,18 @@
|
||||
"login_form_failed_login": "Error logging you in, check server URL, email and password",
|
||||
"login_form_label_email": "Email",
|
||||
"login_form_label_password": "Password",
|
||||
"login_form_password_hint": "password",
|
||||
"login_form_password_hint": "Password",
|
||||
"login_form_save_login": "Stay logged in",
|
||||
"login_form_server_empty": "Enter a server URL.",
|
||||
"login_form_server_error": "Could not connect to server.",
|
||||
"login_form_api_exception": "API exception. Please check the server URL and try again.",
|
||||
"monthly_title_text_date_format": "MMMM y",
|
||||
"notification_permission_dialog_cancel": "Cancel",
|
||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
||||
"notification_permission_dialog_settings": "Settings",
|
||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
||||
"notification_permission_list_tile_title": "Notification Permission",
|
||||
"profile_drawer_app_logs": "Logs",
|
||||
"profile_drawer_client_server_up_to_date": "Client and Server are up-to-date",
|
||||
"profile_drawer_settings": "Settings",
|
||||
@@ -153,6 +183,8 @@
|
||||
"select_additional_user_for_sharing_page_suggestions": "Suggestions",
|
||||
"select_user_for_sharing_page_err_album": "Failed to create album",
|
||||
"select_user_for_sharing_page_share_suggestions": "Suggestions",
|
||||
"server_info_box_app_version": "App Version",
|
||||
"server_info_box_server_version": "Server Version",
|
||||
"setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).",
|
||||
"setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).",
|
||||
"setting_image_viewer_original_title": "Load original image",
|
||||
@@ -202,5 +234,17 @@
|
||||
"version_announcement_overlay_text_1": "Hi friend, there is a new release of",
|
||||
"version_announcement_overlay_text_2": "please take your time to visit the ",
|
||||
"version_announcement_overlay_text_3": " and ensure your docker-compose and .env setup is up-to-date to prevent any misconfigurations, especially if you use WatchTower or any mechanism that handles updating your server application automatically.",
|
||||
"version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89"
|
||||
}
|
||||
"version_announcement_overlay_title": "New Server Version Available \uD83C\uDF89",
|
||||
"permission_onboarding_request": "Immich requires permission to view your photos and videos.",
|
||||
"permission_onboarding_grant_permission": "Grant permission",
|
||||
"permission_onboarding_permission_granted": "Permission granted! You are all set.",
|
||||
"permission_onboarding_permission_denied": "Permission denied. To use Immich, grant photo and video permissions in Settings.",
|
||||
"permission_onboarding_get_started": "Get started",
|
||||
"permission_onboarding_go_to_settings": "Go to settings",
|
||||
"permission_onboarding_permission_limited": "Permission limited. To let Immich backup and manage your entire gallery collection, grant photo and video permissions in Settings.",
|
||||
"permission_onboarding_continue_anyway": "Continue anyway",
|
||||
"permission_onboarding_log_out": "Log out",
|
||||
"login_form_next_button": "Next",
|
||||
"album_thumbnail_shared_by": "Shared by {}",
|
||||
"album_thumbnail_owned": "Owned"
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
||||
"album_info_card_backup_album_excluded": "EXCLUIDOS",
|
||||
"album_info_card_backup_album_included": "INCLUIDOS",
|
||||
"album_thumbnail_card_item": "1 item",
|
||||
@@ -12,10 +14,10 @@
|
||||
"album_viewer_appbar_share_leave": "Abandonar álbum ",
|
||||
"album_viewer_appbar_share_remove": "Eliminar del álbum ",
|
||||
"album_viewer_page_share_add_users": "Añadir usuarios",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamic layout",
|
||||
"asset_list_layout_settings_group_by": "Group assets by",
|
||||
"asset_list_layout_settings_group_by_month": "Month",
|
||||
"asset_list_layout_settings_group_by_month_day": "Month + day",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Disposición dinámica",
|
||||
"asset_list_layout_settings_group_by": "Agrupar recursos por",
|
||||
"asset_list_layout_settings_group_by_month": "Mes",
|
||||
"asset_list_layout_settings_group_by_month_day": "Mes + día",
|
||||
"asset_list_settings_subtitle": "Photo grid layout settings",
|
||||
"asset_list_settings_title": "Photo Grid",
|
||||
"backup_album_selection_page_albums_device": "Álbumes en el dispositivo ({})",
|
||||
@@ -33,6 +35,9 @@
|
||||
"backup_background_service_in_progress_notification": "Backing up your assets…",
|
||||
"backup_background_service_upload_failure_notification": "Failed to upload {}",
|
||||
"backup_controller_page_albums": "Álbumes de copia de seguridad",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "Show me how",
|
||||
"backup_controller_page_background_battery_info_message": "For the best background backup experience, please disable any battery optimizations restricting background activity for Immich.\n\nSince this is device-specific, please lookup the required information for your device manufacturer.",
|
||||
"backup_controller_page_background_battery_info_ok": "OK",
|
||||
@@ -87,11 +92,21 @@
|
||||
"cache_settings_subtitle": "Control the caching behaviour of the Immich mobile application",
|
||||
"cache_settings_thumbnail_size": "Thumbnail cache size ({} assets)",
|
||||
"cache_settings_title": "Caching Settings",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
"common_add_to_album": "Add to album",
|
||||
"common_change_password": "Change Password",
|
||||
"common_create_new_album": "Create new album",
|
||||
"common_shared": "Shared",
|
||||
"control_bottom_app_bar_add_to_album": "Añadir al álbum",
|
||||
"control_bottom_app_bar_album_info": "{} elementos",
|
||||
"control_bottom_app_bar_album_info_shared": "{} elementos · Compartido",
|
||||
"control_bottom_app_bar_create_new_album": "Crear nuevo álbum",
|
||||
"control_bottom_app_bar_delete": "Eliminar",
|
||||
"control_bottom_app_bar_favorite": "Favorite",
|
||||
"control_bottom_app_bar_share": "Share",
|
||||
"create_album_page_untitled": "Untitled",
|
||||
"create_shared_album_page_create": "Create",
|
||||
@@ -112,24 +127,28 @@
|
||||
"experimental_settings_new_asset_list_title": "Enable experimental photo grid",
|
||||
"experimental_settings_subtitle": "Use at your own risk!",
|
||||
"experimental_settings_title": "Experimental",
|
||||
"favorites_page_title": "Favorites",
|
||||
"favorites_page_title": "Favoritos",
|
||||
"home_page_add_to_album_conflicts": "Añadidos {added} elementos al álbum {album}. {failed} elementos ya están añadidos.",
|
||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
||||
"home_page_add_to_album_success": "Añadidos {added} elementos al álbum {album}.",
|
||||
"home_page_building_timeline": "Building the timeline",
|
||||
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
|
||||
"home_page_building_timeline": "Construyendo la línea de tiempo",
|
||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
||||
"home_page_first_time_notice": "Si esta es la primera vez que usas la app, por favor, asegúrate de elegir un álbum de respaldo para que la línea de tiempo pueda cargar fotos y videos en los álbumes.",
|
||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
||||
"library_page_albums": "Albums",
|
||||
"library_page_favorites": "Favorites",
|
||||
"library_page_favorites": "Favoritos",
|
||||
"library_page_new_album": "New album",
|
||||
"library_page_sharing": "Sharing",
|
||||
"library_page_sort_created": "Most recently created",
|
||||
"library_page_sort_title": "Album title",
|
||||
"library_page_sharing": "Compartiendo",
|
||||
"library_page_sort_created": "Creado más recientemente",
|
||||
"library_page_sort_title": "Título del álbum",
|
||||
"login_form_button_text": "Iniciar Sesión",
|
||||
"login_form_email_hint": "tucorreo@correo.com",
|
||||
"login_form_endpoint_hint": "http://tu-ip-de-servidor:puerto/api",
|
||||
"login_form_endpoint_url": "URL del servidor",
|
||||
"login_form_err_http": "Por favor, especifique http:// o https://",
|
||||
"login_form_err_invalid_email": "Correo electrónico no válido",
|
||||
"login_form_err_invalid_url": "Invalid URL",
|
||||
"login_form_err_invalid_url": "URL no válida",
|
||||
"login_form_err_leading_whitespace": "Espacio en blanco inicial",
|
||||
"login_form_err_trailing_whitespace": "Espacio en blanco al final",
|
||||
"login_form_failed_get_oauth_server_config": "Error logging using OAuth, check server URL",
|
||||
@@ -140,6 +159,12 @@
|
||||
"login_form_password_hint": "contraseña",
|
||||
"login_form_save_login": "Mantener la sesión iniciada",
|
||||
"monthly_title_text_date_format": "MMMM y",
|
||||
"notification_permission_dialog_cancel": "Cancel",
|
||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
||||
"notification_permission_dialog_settings": "Settings",
|
||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
||||
"notification_permission_list_tile_title": "Notification Permission",
|
||||
"profile_drawer_app_logs": "Logs",
|
||||
"profile_drawer_client_server_up_to_date": "El Cliente y el Servidor están actualizados",
|
||||
"profile_drawer_settings": "Settings",
|
||||
@@ -153,6 +178,8 @@
|
||||
"select_additional_user_for_sharing_page_suggestions": "Sugerencias",
|
||||
"select_user_for_sharing_page_err_album": "Fallo al crear el álbum",
|
||||
"select_user_for_sharing_page_share_suggestions": "Suggestions",
|
||||
"server_info_box_app_version": "App Version",
|
||||
"server_info_box_server_version": "Server Version",
|
||||
"setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).",
|
||||
"setting_image_viewer_original_subtitle": "Habilitar para cargar la imagen en resolución original (¡muy grande!). Deshabilitar para reducir el consumo de datos (de red y caché).",
|
||||
"setting_image_viewer_original_title": "Cargar imagen original",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
||||
"album_info_card_backup_album_excluded": "JÄTETTY POIS",
|
||||
"album_info_card_backup_album_included": "SISÄLLYTETTY",
|
||||
"album_thumbnail_card_item": "1 kohde",
|
||||
@@ -12,10 +14,10 @@
|
||||
"album_viewer_appbar_share_leave": "Poistu albumista",
|
||||
"album_viewer_appbar_share_remove": "Poista albumista",
|
||||
"album_viewer_page_share_add_users": "Lisää käyttäjiä",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamic layout",
|
||||
"asset_list_layout_settings_group_by": "Group assets by",
|
||||
"asset_list_layout_settings_group_by_month": "Month",
|
||||
"asset_list_layout_settings_group_by_month_day": "Month + day",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Dynaaminen asetelma",
|
||||
"asset_list_layout_settings_group_by": "Ryhmittele",
|
||||
"asset_list_layout_settings_group_by_month": "Kuukauden mukaan",
|
||||
"asset_list_layout_settings_group_by_month_day": "Kuukauden ja päivän mukaan",
|
||||
"asset_list_settings_subtitle": "Kuvaruudukon asettelu",
|
||||
"asset_list_settings_title": "Kuvaruudukko",
|
||||
"backup_album_selection_page_albums_device": "Laitteen albumit ({})",
|
||||
@@ -33,6 +35,9 @@
|
||||
"backup_background_service_in_progress_notification": "Varmuuskopioidaan kohteita...",
|
||||
"backup_background_service_upload_failure_notification": "Lähetys palvelimelle epäonnistui {}",
|
||||
"backup_controller_page_albums": "Varmuuskopioi albumit",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "Näytä minulle miten",
|
||||
"backup_controller_page_background_battery_info_message": "Kytke pois päältä kaikki Immichin taustatyöskentelyyn liittyvät akun optimoinnit, jotta varmistat taustavarmuuskopioinnin parhaan mahdollisen toiminnan.\n\nKoska tämä on laitekohtaista, tarkista tarvittavat toimet laitevalmistajan ohjeista.",
|
||||
"backup_controller_page_background_battery_info_ok": "OK",
|
||||
@@ -87,11 +92,21 @@
|
||||
"cache_settings_subtitle": "Hallitse Immich-mobiilisovelluksen välimuistin käyttöä",
|
||||
"cache_settings_thumbnail_size": "Esikatselukuvien välimuistin koko ({} kohdetta)",
|
||||
"cache_settings_title": "Välimuistin asetukset",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
"common_add_to_album": "Add to album",
|
||||
"common_change_password": "Change Password",
|
||||
"common_create_new_album": "Create new album",
|
||||
"common_shared": "Shared",
|
||||
"control_bottom_app_bar_add_to_album": "Lisää albumiin",
|
||||
"control_bottom_app_bar_album_info": "{} kohdetta",
|
||||
"control_bottom_app_bar_album_info_shared": "{} kohdetta · Jaettu",
|
||||
"control_bottom_app_bar_create_new_album": "Luo uusi albumi",
|
||||
"control_bottom_app_bar_delete": "Poista",
|
||||
"control_bottom_app_bar_favorite": "Favorite",
|
||||
"control_bottom_app_bar_share": "Jaa",
|
||||
"create_album_page_untitled": "Nimetön",
|
||||
"create_shared_album_page_create": "Luo",
|
||||
@@ -112,24 +127,28 @@
|
||||
"experimental_settings_new_asset_list_title": "Ota käyttöön kokeellinen kuvaruudukko",
|
||||
"experimental_settings_subtitle": "Käyttö omalla vastuulla!",
|
||||
"experimental_settings_title": "Kokeellinen",
|
||||
"favorites_page_title": "Favorites",
|
||||
"favorites_page_title": "Suosikit",
|
||||
"home_page_add_to_album_conflicts": "Lisätty {added} kohdetta albumiin {album}. {failed} kohdetta on jo albumissa.",
|
||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
||||
"home_page_add_to_album_success": "Lisätty {added} kohdetta albumiin {album}.",
|
||||
"home_page_building_timeline": "Building the timeline",
|
||||
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
|
||||
"home_page_building_timeline": "Rakennetaan aikajanaa",
|
||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
||||
"home_page_first_time_notice": "Jos käytät sovellusta ensimmäistä kertaa, muista valita varmuuskopioitavat albumi(t), jotta aikajanalla voi olla kuvia ja videoita.",
|
||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
||||
"library_page_albums": "Albumit",
|
||||
"library_page_favorites": "Favorites",
|
||||
"library_page_favorites": "Suosikit",
|
||||
"library_page_new_album": "Uusi albumi",
|
||||
"library_page_sharing": "Sharing",
|
||||
"library_page_sort_created": "Most recently created",
|
||||
"library_page_sort_title": "Album title",
|
||||
"library_page_sharing": "Jakaminen",
|
||||
"library_page_sort_created": "Viimeisin luotu",
|
||||
"library_page_sort_title": "Albumin otsikko",
|
||||
"login_form_button_text": "Kirjaudu",
|
||||
"login_form_email_hint": "sahkopostisi@esimerkki.fi",
|
||||
"login_form_endpoint_hint": "http://palvelimesi-osoite:portti/api",
|
||||
"login_form_endpoint_url": "Palvelimen URL",
|
||||
"login_form_err_http": "Lisää http:// tai https://",
|
||||
"login_form_err_invalid_email": "Virheellinen sähköpostiosoite",
|
||||
"login_form_err_invalid_url": "Invalid URL",
|
||||
"login_form_err_invalid_url": "Virheellinen URL",
|
||||
"login_form_err_leading_whitespace": "Alussa välilyönti",
|
||||
"login_form_err_trailing_whitespace": "Lopussa välilyönti",
|
||||
"login_form_failed_get_oauth_server_config": "Virhe kirjauduttaessa OAuth:lla, tarkista palvelimen URL",
|
||||
@@ -140,6 +159,12 @@
|
||||
"login_form_password_hint": "salasana",
|
||||
"login_form_save_login": "Pysy kirjautuneena",
|
||||
"monthly_title_text_date_format": "MMMM y",
|
||||
"notification_permission_dialog_cancel": "Cancel",
|
||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
||||
"notification_permission_dialog_settings": "Settings",
|
||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
||||
"notification_permission_list_tile_title": "Notification Permission",
|
||||
"profile_drawer_app_logs": "Lokit",
|
||||
"profile_drawer_client_server_up_to_date": "Asiakassovellus ja palvelin ovat ajan tasalla",
|
||||
"profile_drawer_settings": "Asetukset",
|
||||
@@ -153,6 +178,8 @@
|
||||
"select_additional_user_for_sharing_page_suggestions": "Ehdotukset",
|
||||
"select_user_for_sharing_page_err_album": "Albumin luonti epäonnistui",
|
||||
"select_user_for_sharing_page_share_suggestions": "Ehdotukset",
|
||||
"server_info_box_app_version": "App Version",
|
||||
"server_info_box_server_version": "Server Version",
|
||||
"setting_image_viewer_help": "Sovellus lataa ensin pienen esikatselukuvan, toisena keskitarkkuuksisen kuvan (jos käytössä) ja kolmantena alkuperäisen täysitarkkuuksisen kuvan (jos käytössä)",
|
||||
"setting_image_viewer_original_subtitle": "Ota käyttöön ladataksesi alkuperäinen täysitarkkuuksinen kuva (suuri!). Poista käytöstä vähentääksesi datan käyttöä (sekä verkossa että laitteen välimuistissa).",
|
||||
"setting_image_viewer_original_title": "Lataa alkuperäinen kuva",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
||||
"album_info_card_backup_album_excluded": "EXCLU",
|
||||
"album_info_card_backup_album_included": "INCLUS",
|
||||
"album_thumbnail_card_item": "1 élément",
|
||||
@@ -12,10 +14,10 @@
|
||||
"album_viewer_appbar_share_leave": "Quitter l'album",
|
||||
"album_viewer_appbar_share_remove": "Retirer de l'album",
|
||||
"album_viewer_page_share_add_users": "Ajouter des utilisateurs",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamic layout",
|
||||
"asset_list_layout_settings_group_by": "Group assets by",
|
||||
"asset_list_layout_settings_group_by_month": "Month",
|
||||
"asset_list_layout_settings_group_by_month_day": "Month + day",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Affichage dynamique",
|
||||
"asset_list_layout_settings_group_by": "Grouper les éléments par",
|
||||
"asset_list_layout_settings_group_by_month": "Mois",
|
||||
"asset_list_layout_settings_group_by_month_day": "Mois + jour",
|
||||
"asset_list_settings_subtitle": "Paramètres de disposition de la grille de photos",
|
||||
"asset_list_settings_title": "Grille de photos",
|
||||
"backup_album_selection_page_albums_device": "Albums sur l'appareil ({})",
|
||||
@@ -33,6 +35,9 @@
|
||||
"backup_background_service_in_progress_notification": "Sauvegarde de vos éléments...",
|
||||
"backup_background_service_upload_failure_notification": "Impossible de transférer {}",
|
||||
"backup_controller_page_albums": "Sauvegarder les albums",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "Montrez-moi comment",
|
||||
"backup_controller_page_background_battery_info_message": "Pour une expérience optimale de la sauvegarde en arrière-plan, veuillez désactiver toute optimisation de la batterie limitant l'activité en arrière-plan pour Immich.\n\nÉtant donné que cela est spécifique à chaque appareil, veuillez consulter les informations requises pour le fabricant de votre appareil.",
|
||||
"backup_controller_page_background_battery_info_ok": "OK",
|
||||
@@ -87,11 +92,21 @@
|
||||
"cache_settings_subtitle": "Contrôler le comportement de mise en cache de l'application mobile Immich",
|
||||
"cache_settings_thumbnail_size": "Taille du cache des miniatures ({} éléments)",
|
||||
"cache_settings_title": "Paramètres de mise en cache",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
"common_add_to_album": "Add to album",
|
||||
"common_change_password": "Change Password",
|
||||
"common_create_new_album": "Create new album",
|
||||
"common_shared": "Shared",
|
||||
"control_bottom_app_bar_add_to_album": "Ajouter à l'album",
|
||||
"control_bottom_app_bar_album_info": "{} éléments",
|
||||
"control_bottom_app_bar_album_info_shared": "{} éléments - Partagés",
|
||||
"control_bottom_app_bar_create_new_album": "Créer un nouvel album",
|
||||
"control_bottom_app_bar_delete": "Supprimer",
|
||||
"control_bottom_app_bar_favorite": "Favorite",
|
||||
"control_bottom_app_bar_share": "Partager",
|
||||
"create_album_page_untitled": "Sans titre",
|
||||
"create_shared_album_page_create": "Créer",
|
||||
@@ -112,17 +127,21 @@
|
||||
"experimental_settings_new_asset_list_title": "Activez la grille de photos expérimentale",
|
||||
"experimental_settings_subtitle": "Utilisez à vos dépends !",
|
||||
"experimental_settings_title": "Expérimental",
|
||||
"favorites_page_title": "Favorites",
|
||||
"favorites_page_title": "Favoris",
|
||||
"home_page_add_to_album_conflicts": "{added} éléments ajoutés à l'album {album}. Les éléments {failed} sont déjà dans l'album.",
|
||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
||||
"home_page_add_to_album_success": "{added} éléments ajoutés à l'album {album}.",
|
||||
"home_page_building_timeline": "Building the timeline",
|
||||
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
|
||||
"home_page_building_timeline": "Construction de la chronologie",
|
||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
||||
"home_page_first_time_notice": "Si c'est la première fois que vous utilisez l'application, veillez à choisir un ou plusieurs albums de sauvegarde afin que la chronologie puisse alimenter les photos et les vidéos de cet ou ces albums.",
|
||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
||||
"library_page_albums": "Albums",
|
||||
"library_page_favorites": "Favorites",
|
||||
"library_page_favorites": "Favoris",
|
||||
"library_page_new_album": "Nouvel album",
|
||||
"library_page_sharing": "Sharing",
|
||||
"library_page_sort_created": "Most recently created",
|
||||
"library_page_sort_title": "Album title",
|
||||
"library_page_sharing": "Partage",
|
||||
"library_page_sort_created": "Créations les plus récentes",
|
||||
"library_page_sort_title": "Titre de l'album",
|
||||
"login_form_button_text": "Connexion",
|
||||
"login_form_email_hint": "votreemail@email.com",
|
||||
"login_form_endpoint_hint": "http://adresse-ip-serveur:port/api",
|
||||
@@ -140,6 +159,12 @@
|
||||
"login_form_password_hint": "mot de passe",
|
||||
"login_form_save_login": "Rester connecté",
|
||||
"monthly_title_text_date_format": "MMMM y",
|
||||
"notification_permission_dialog_cancel": "Cancel",
|
||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
||||
"notification_permission_dialog_settings": "Settings",
|
||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
||||
"notification_permission_list_tile_title": "Notification Permission",
|
||||
"profile_drawer_app_logs": "Journaux",
|
||||
"profile_drawer_client_server_up_to_date": "Le client et le serveur sont à jour",
|
||||
"profile_drawer_settings": "Paramètres",
|
||||
@@ -153,8 +178,10 @@
|
||||
"select_additional_user_for_sharing_page_suggestions": "Suggestions",
|
||||
"select_user_for_sharing_page_err_album": "Échec de la création de l'album",
|
||||
"select_user_for_sharing_page_share_suggestions": "Suggestions",
|
||||
"server_info_box_app_version": "App Version",
|
||||
"server_info_box_server_version": "Server Version",
|
||||
"setting_image_viewer_help": "Le visualiseur de détails charge d'abord la petite miniature, puis l'aperçu de taille moyenne (s'il est activé), enfin l'original (s'il est activé).",
|
||||
"setting_image_viewer_original_subtitle": "Activez cette option pour charger l'image originale en résolution originale (volumineux !). Désactiver pour réduire l'utilisation des données (réseau et cache de l'appareil).",
|
||||
"setting_image_viewer_original_subtitle": "Activez cette option pour charger l'image en résolution originale (volumineux !). Désactiver pour réduire l'utilisation des données (réseau et cache de l'appareil).",
|
||||
"setting_image_viewer_original_title": "Charger l'image originale",
|
||||
"setting_image_viewer_preview_subtitle": "Activer pour charger une image de résolution moyenne. Désactiver pour charger directement l'original ou utiliser uniquement la miniature.",
|
||||
"setting_image_viewer_preview_title": "Charger l'image d'aperçu",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
||||
"album_info_card_backup_album_excluded": "ESCLUSI",
|
||||
"album_info_card_backup_album_included": "INCLUSI",
|
||||
"album_thumbnail_card_item": "1 elemento ",
|
||||
@@ -33,6 +35,9 @@
|
||||
"backup_background_service_in_progress_notification": "Backing dei tuoi contenuti…",
|
||||
"backup_background_service_upload_failure_notification": "Impossibile caricare {}",
|
||||
"backup_controller_page_albums": "Backup Album",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "Mostrami come",
|
||||
"backup_controller_page_background_battery_info_message": "Per una migliore esperienza di backup, disabilita le ottimizzazioni della batteria per l'app Immich.\n\nDal momento che è una funzionalità specifica del dispositivo, per favore consulta il manuale del produttore.",
|
||||
"backup_controller_page_background_battery_info_ok": "OK",
|
||||
@@ -87,11 +92,21 @@
|
||||
"cache_settings_subtitle": "Controlla il comportamento della cache dell'applicazione mobile immich",
|
||||
"cache_settings_thumbnail_size": "Dimensione cache anteprime ({} assets)",
|
||||
"cache_settings_title": "Impostazioni della Cache",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
"common_add_to_album": "Add to album",
|
||||
"common_change_password": "Change Password",
|
||||
"common_create_new_album": "Create new album",
|
||||
"common_shared": "Shared",
|
||||
"control_bottom_app_bar_add_to_album": "Aggiungi all'album",
|
||||
"control_bottom_app_bar_album_info": "{} elementi",
|
||||
"control_bottom_app_bar_album_info_shared": "{} elementi · Condivisi",
|
||||
"control_bottom_app_bar_create_new_album": "Crea nuovo album",
|
||||
"control_bottom_app_bar_delete": "Elimina",
|
||||
"control_bottom_app_bar_favorite": "Favorite",
|
||||
"control_bottom_app_bar_share": "Condividi",
|
||||
"create_album_page_untitled": "Senza titolo",
|
||||
"create_shared_album_page_create": "Crea",
|
||||
@@ -114,9 +129,13 @@
|
||||
"experimental_settings_title": "Sperimentale",
|
||||
"favorites_page_title": "Favorites",
|
||||
"home_page_add_to_album_conflicts": "Aggiunti {added} elementi all'album {album}. {failed} elementi erano già presenti nell'album.",
|
||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
||||
"home_page_add_to_album_success": "Aggiunti {added} elementi all'album {album}",
|
||||
"home_page_building_timeline": "Building the timeline",
|
||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
||||
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
|
||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
||||
"library_page_albums": "Album",
|
||||
"library_page_favorites": "Favorites",
|
||||
"library_page_new_album": "Nuovo Album",
|
||||
@@ -140,6 +159,12 @@
|
||||
"login_form_password_hint": "password ",
|
||||
"login_form_save_login": "Rimani connesso ",
|
||||
"monthly_title_text_date_format": "MMMM y",
|
||||
"notification_permission_dialog_cancel": "Cancel",
|
||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
||||
"notification_permission_dialog_settings": "Settings",
|
||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
||||
"notification_permission_list_tile_title": "Notification Permission",
|
||||
"profile_drawer_app_logs": "Logs",
|
||||
"profile_drawer_client_server_up_to_date": "Client e server sono aggiornati",
|
||||
"profile_drawer_settings": "Impostazioni ",
|
||||
@@ -153,6 +178,8 @@
|
||||
"select_additional_user_for_sharing_page_suggestions": "Suggerimenti ",
|
||||
"select_user_for_sharing_page_err_album": "Impossibile nel creare l'album ",
|
||||
"select_user_for_sharing_page_share_suggestions": "Suggerimenti",
|
||||
"server_info_box_app_version": "App Version",
|
||||
"server_info_box_server_version": "Server Version",
|
||||
"setting_image_viewer_help": "Il visualizzatore dettagliato carica una piccola thumbnail per prima, per poi caricare un immagine di media grandezza (se abilitato). Ed infine carica l'originale (se abilitato).",
|
||||
"setting_image_viewer_original_subtitle": "Abilita per caricare l'immagine originale a risoluzione massima (grande!). Disabilita per ridurre l'utilizzo di banda (sia sul network che nella cache del dispositivo).",
|
||||
"setting_image_viewer_original_title": "Carica l'immagine originale",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
||||
"album_info_card_backup_album_excluded": "除外中",
|
||||
"album_info_card_backup_album_included": "選択中",
|
||||
"album_thumbnail_card_item": "項目数: 1",
|
||||
@@ -33,6 +35,9 @@
|
||||
"backup_background_service_in_progress_notification": "バックアップ中",
|
||||
"backup_background_service_upload_failure_notification": "{} のアップロードに失敗",
|
||||
"backup_controller_page_albums": "アルバム",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "方法を見る",
|
||||
"backup_controller_page_background_battery_info_message": "バックグラウンドバックアップが正常に動作するためにImmichに適用されてるバッテリーの最適化と自動調整をオフにしてね。\n\n端末によって方法が変わるから各々調べてね",
|
||||
"backup_controller_page_background_battery_info_ok": "了解",
|
||||
@@ -87,11 +92,21 @@
|
||||
"cache_settings_subtitle": "キャッシュの動作を変更できるよ",
|
||||
"cache_settings_thumbnail_size": "サムネイルのキャッシュのサイズ ({}枚)",
|
||||
"cache_settings_title": "キャッシュの設定",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
"common_add_to_album": "Add to album",
|
||||
"common_change_password": "Change Password",
|
||||
"common_create_new_album": "Create new album",
|
||||
"common_shared": "Shared",
|
||||
"control_bottom_app_bar_add_to_album": "Add to album",
|
||||
"control_bottom_app_bar_album_info": "{} items",
|
||||
"control_bottom_app_bar_album_info_shared": "{} items · Shared",
|
||||
"control_bottom_app_bar_create_new_album": "Create new album",
|
||||
"control_bottom_app_bar_delete": "削除",
|
||||
"control_bottom_app_bar_favorite": "Favorite",
|
||||
"control_bottom_app_bar_share": "共有",
|
||||
"create_album_page_untitled": "タイトル無し",
|
||||
"create_shared_album_page_create": "作成",
|
||||
@@ -114,9 +129,13 @@
|
||||
"experimental_settings_title": "試験的",
|
||||
"favorites_page_title": "Favorites",
|
||||
"home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.",
|
||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
||||
"home_page_add_to_album_success": "Added {added} assets to album {album}.",
|
||||
"home_page_building_timeline": "Building the timeline",
|
||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
||||
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
|
||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
||||
"library_page_albums": "アルバム",
|
||||
"library_page_favorites": "Favorites",
|
||||
"library_page_new_album": "新しいアルバム",
|
||||
@@ -140,6 +159,12 @@
|
||||
"login_form_password_hint": "パスワード",
|
||||
"login_form_save_login": "ログインしたままにする",
|
||||
"monthly_title_text_date_format": "yyyy年 MM月",
|
||||
"notification_permission_dialog_cancel": "Cancel",
|
||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
||||
"notification_permission_dialog_settings": "Settings",
|
||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
||||
"notification_permission_list_tile_title": "Notification Permission",
|
||||
"profile_drawer_app_logs": "Logs",
|
||||
"profile_drawer_client_server_up_to_date": "サーバーとクライアント、両方最新バージョンだよ",
|
||||
"profile_drawer_settings": "設定",
|
||||
@@ -153,6 +178,8 @@
|
||||
"select_additional_user_for_sharing_page_suggestions": "ユーザーリスト",
|
||||
"select_user_for_sharing_page_err_album": "アルバム作成に失敗...",
|
||||
"select_user_for_sharing_page_share_suggestions": "ユーザーの一覧",
|
||||
"server_info_box_app_version": "App Version",
|
||||
"server_info_box_server_version": "Server Version",
|
||||
"setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).",
|
||||
"setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).",
|
||||
"setting_image_viewer_original_title": "Load original image",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "{album}에 추가",
|
||||
"add_to_album_bottom_sheet_already_exists": "{album}에 이미 포함되어 있습니다",
|
||||
"album_info_card_backup_album_excluded": "제외됨",
|
||||
"album_info_card_backup_album_included": "포함됨",
|
||||
"album_thumbnail_card_item": "1개 항목",
|
||||
@@ -12,6 +14,10 @@
|
||||
"album_viewer_appbar_share_leave": "앨범 나가기",
|
||||
"album_viewer_appbar_share_remove": "앨범에서 제거",
|
||||
"album_viewer_page_share_add_users": "사용자 추가",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "다이나믹 레이아웃",
|
||||
"asset_list_layout_settings_group_by": "다음으로 그룹화",
|
||||
"asset_list_layout_settings_group_by_month": "월",
|
||||
"asset_list_layout_settings_group_by_month_day": "월 + 일",
|
||||
"asset_list_settings_subtitle": "사진 배열 레이아웃 설정",
|
||||
"asset_list_settings_title": "사진 배열",
|
||||
"backup_album_selection_page_albums_device": "기기의 앨범({})",
|
||||
@@ -29,12 +35,16 @@
|
||||
"backup_background_service_in_progress_notification": "미디어파일 백업 중...",
|
||||
"backup_background_service_upload_failure_notification": "{} 업로드 실패",
|
||||
"backup_controller_page_albums": "백업대상",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "사용 가이드",
|
||||
"backup_controller_page_background_battery_info_message": "최상의 백업 환경을 위해 Immich 앱의 백그라운드 활동을 제한하는 배터리 최적화기능을 꺼주세요.\n\n휴대폰마다 설정방법이 다르므로 제조업체별로 설정방법을 확인하세요.",
|
||||
"backup_controller_page_background_battery_info_ok": "확인",
|
||||
"backup_controller_page_background_battery_info_title": "배터리 최적화",
|
||||
"backup_controller_page_background_charging": "충전 중일 때만",
|
||||
"backup_controller_page_background_configure_error": "백그라운드 서비스를 구성하지 못했습니다",
|
||||
"backup_controller_page_background_delay": "새 미디어파일 백업 지연: {}",
|
||||
"backup_controller_page_background_description": "백그라운드 서비스를 켜서 앱을 열지 않고도 새 미디어파일을 자동으로 백업합니다.",
|
||||
"backup_controller_page_background_is_off": "자동 백그라운드 백업이 꺼져 있습니다",
|
||||
"backup_controller_page_background_is_on": "자동 백그라운드 백업이 켜져 있습니다",
|
||||
@@ -82,11 +92,21 @@
|
||||
"cache_settings_subtitle": "Immich 앱의 캐싱 동작 제어",
|
||||
"cache_settings_thumbnail_size": "썸네일 캐시 크기 ({} 미디어)",
|
||||
"cache_settings_title": "캐시 설정",
|
||||
"change_password_form_confirm_password": "비밀번호 확인",
|
||||
"change_password_form_description": "{firstName} {lastName} 님, 안녕하세요.\n\n시스템에 처음 로그인했거나 비밀번호 변경 요청이 있었습니다. 아래에 새 비밀번호를 입력하세요.",
|
||||
"change_password_form_new_password": "새 비밀번호",
|
||||
"change_password_form_password_mismatch": "비밀번호가 일치하지 않습니다",
|
||||
"change_password_form_reenter_new_password": "새 비밀번호 재입력",
|
||||
"common_add_to_album": "앨범에 추가",
|
||||
"common_change_password": "비밀번호 변경",
|
||||
"common_create_new_album": "새 앨범 만들기",
|
||||
"common_shared": "공유됨",
|
||||
"control_bottom_app_bar_add_to_album": "앨범에 추가",
|
||||
"control_bottom_app_bar_album_info": "{} 항목",
|
||||
"control_bottom_app_bar_album_info_shared": "{} 항목 · 공유됨",
|
||||
"control_bottom_app_bar_create_new_album": "앨범 생성",
|
||||
"control_bottom_app_bar_delete": "삭제",
|
||||
"control_bottom_app_bar_favorite": "즐겨찾기",
|
||||
"control_bottom_app_bar_share": "공유",
|
||||
"create_album_page_untitled": "제목없음",
|
||||
"create_shared_album_page_create": "만들기",
|
||||
@@ -103,26 +123,49 @@
|
||||
"exif_bottom_sheet_description": "설명 추가...",
|
||||
"exif_bottom_sheet_details": "상세정보",
|
||||
"exif_bottom_sheet_location": "위치",
|
||||
"experimental_settings_new_asset_list_subtitle": "진행중",
|
||||
"experimental_settings_new_asset_list_title": "실험적 사진 그리드 적용",
|
||||
"experimental_settings_subtitle": "문제시 책임지지 않습니다!",
|
||||
"experimental_settings_title": "실험적기능",
|
||||
"favorites_page_title": "즐겨찾기",
|
||||
"home_page_add_to_album_conflicts": "{album} 앨범에 {added} 미디어를 추가했습니다. {failed} 이미 앨범에 있는 항목입니다.",
|
||||
"home_page_add_to_album_err_local": "앨범에 미디어파일을 추가할 수 없어, 건너뜁니다.",
|
||||
"home_page_add_to_album_success": "{album} 앨범에 {added} 미디어를 추가했습니다. ",
|
||||
"home_page_building_timeline": "타임라인 생성",
|
||||
"home_page_favorite_err_local": "미디어파일을 즐겨찾기에 추가할 수 없어, 건너뜁니다.",
|
||||
"home_page_first_time_notice": "앱을 처음 사용하는 경우 타임라인이 앨범의 사진과 비디오를 채울 수 있도록 백업대상 앨범을 선택해야 합니다.",
|
||||
"image_viewer_page_state_provider_download_error": "다운로드 에러",
|
||||
"image_viewer_page_state_provider_download_success": "다운로드 완료",
|
||||
"library_page_albums": "앨범",
|
||||
"library_page_favorites": "즐겨찾기",
|
||||
"library_page_new_album": "새 앨범",
|
||||
"library_page_sharing": "공유",
|
||||
"library_page_sort_created": "최근생성일",
|
||||
"library_page_sort_title": "앨범 제목",
|
||||
"login_form_button_text": "로그인",
|
||||
"login_form_email_hint": "youremail@email.com",
|
||||
"login_form_endpoint_hint": "https://your-server-ip:port/api",
|
||||
"login_form_endpoint_url": "서버 엔드포인트 URL",
|
||||
"login_form_err_http": "엔드포인트는 http:// 또는 https://로 시작해야 합니다",
|
||||
"login_form_err_invalid_email": "잘못된 이메일 형식입니다",
|
||||
"login_form_err_invalid_url": "잘못된 URL 형식입니다",
|
||||
"login_form_err_leading_whitespace": "이메일 앞에 공백문자가 포함되어 있습니다",
|
||||
"login_form_err_trailing_whitespace": "이메일 뒤에 공백문자가 포함되어 있습니다",
|
||||
"login_form_failed_get_oauth_server_config": "OAuth 로그인 오류, 서버 URL을 확인해주세요",
|
||||
"login_form_failed_get_oauth_server_disable": "이 서버에서는 OAuth 기능을 사용할 수 없습니다.",
|
||||
"login_form_failed_login": "로그인 오류, 서버 URL, 이메일 및 비밀번호를 확인하세요",
|
||||
"login_form_label_email": "이메일",
|
||||
"login_form_label_password": "비밀번호",
|
||||
"login_form_password_hint": "비밀번호",
|
||||
"login_form_save_login": "로그인상태 유지",
|
||||
"monthly_title_text_date_format": "y년 M월",
|
||||
"notification_permission_dialog_cancel": "취소",
|
||||
"notification_permission_dialog_content": "알림을 활성화하려면 설정으로 이동하여 허용을 선택해주세요.",
|
||||
"notification_permission_dialog_settings": "설정",
|
||||
"notification_permission_list_tile_content": "알림 활성화 권한허용",
|
||||
"notification_permission_list_tile_enable_button": "알림 활성화",
|
||||
"notification_permission_list_tile_title": "알림 권한",
|
||||
"profile_drawer_app_logs": "로그",
|
||||
"profile_drawer_client_server_up_to_date": "클라이언트와 서버가 최신 상태입니다",
|
||||
"profile_drawer_settings": "설정",
|
||||
"profile_drawer_sign_out": "로그아웃",
|
||||
@@ -135,11 +178,19 @@
|
||||
"select_additional_user_for_sharing_page_suggestions": "초대 가능한 사용자 제안",
|
||||
"select_user_for_sharing_page_err_album": "앨범 생성 실패",
|
||||
"select_user_for_sharing_page_share_suggestions": "초대 가능한 사용자 제안",
|
||||
"server_info_box_app_version": "앱 버전",
|
||||
"server_info_box_server_version": "서버 버전",
|
||||
"setting_image_viewer_help": "상세뷰어는 먼저 작은 썸네일을 불러온 다음 중간크기 미리보기를 불러오고(활성화된 경우) 마지막으로 원본을 불러옵니다(활성화된 경우).",
|
||||
"setting_image_viewer_original_subtitle": "원본 해상도 이미지(고화질)를 로드하려면 활성화합니다. 데이터 사용량을 줄이려면 비활성화합니다.",
|
||||
"setting_image_viewer_original_title": "원본 이미지 불러오기",
|
||||
"setting_image_viewer_preview_subtitle": "중간 해상도 이미지를 로드하려면 활성화합니다. 원본을 직접 로드하거나 썸네일만 사용하려면 비활성화 하세요.",
|
||||
"setting_image_viewer_preview_title": "미리보기 이미지 불러오기",
|
||||
"setting_notifications_notify_failures_grace_period": "백그라운드 백업 실패 알림: {}",
|
||||
"setting_notifications_notify_hours": "{}시간 뒤",
|
||||
"setting_notifications_notify_immediately": "즉시",
|
||||
"setting_notifications_notify_minutes": "{}분 뒤",
|
||||
"setting_notifications_notify_never": "알리지 않음",
|
||||
"setting_notifications_notify_seconds": "{} 초",
|
||||
"setting_notifications_single_progress_subtitle": "미디어별 상세 진행률 표시",
|
||||
"setting_notifications_single_progress_title": "백그라운드 작업 세부 진행률 표시",
|
||||
"setting_notifications_subtitle": "알림 기본 설정 조정",
|
||||
@@ -147,6 +198,7 @@
|
||||
"setting_notifications_total_progress_subtitle": "전체 업로드 진행률(완료/전체)",
|
||||
"setting_notifications_total_progress_title": "백그라운드 작업 전체 진행률 표시",
|
||||
"setting_pages_app_bar_settings": "설정",
|
||||
"settings_require_restart": "설정을 적용하려면 Immich를 다시 시작하세요.",
|
||||
"share_add": "추가",
|
||||
"share_add_photos": "사진 추가",
|
||||
"share_add_title": "새 앨범제목",
|
||||
@@ -177,5 +229,5 @@
|
||||
"version_announcement_overlay_text_1": "안녕하세요!",
|
||||
"version_announcement_overlay_text_2": "앱에 새로운 업데이트가 있습니다!",
|
||||
"version_announcement_overlay_text_3": "특히 WatchTower 또는 서버 응용 프로그램 자동 업데이트를 처리하는 메커니즘을 사용하는 경우 잘못된 구성을 방지하기 위해 docker-compose 및 .env 설정이 최신 상태인지 확인하세요.",
|
||||
"version_announcement_overlay_title": "새 서버 버전 사용 가능 🎉"
|
||||
"version_announcement_overlay_title": "새 서버 버전 사용 가능 \uD83C\uDF89"
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Toegevoegd aan {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Staat al in {album}",
|
||||
"album_info_card_backup_album_excluded": "UITGESLOTEN",
|
||||
"album_info_card_backup_album_included": "INGESLOTEN",
|
||||
"album_thumbnail_card_item": "1 item",
|
||||
@@ -12,10 +14,10 @@
|
||||
"album_viewer_appbar_share_leave": "Verlaat album",
|
||||
"album_viewer_appbar_share_remove": "Verwijder uit album",
|
||||
"album_viewer_page_share_add_users": "Gebruikers toevoegen",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamic layout",
|
||||
"asset_list_layout_settings_group_by": "Group assets by",
|
||||
"asset_list_layout_settings_group_by_month": "Month",
|
||||
"asset_list_layout_settings_group_by_month_day": "Month + day",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamische layout",
|
||||
"asset_list_layout_settings_group_by": "Groupeer items per",
|
||||
"asset_list_layout_settings_group_by_month": "Maand",
|
||||
"asset_list_layout_settings_group_by_month_day": "Maand + dag",
|
||||
"asset_list_settings_subtitle": "Foto grid layout instellingen",
|
||||
"asset_list_settings_title": "Foto Grid",
|
||||
"backup_album_selection_page_albums_device": "Albums op apparaat ({})",
|
||||
@@ -33,6 +35,9 @@
|
||||
"backup_background_service_in_progress_notification": "Back-up maken van items…",
|
||||
"backup_background_service_upload_failure_notification": "Fout bij upload {}",
|
||||
"backup_controller_page_albums": "Back-up albums",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "Toon me hoe",
|
||||
"backup_controller_page_background_battery_info_message": "Schakel voor de beste back-up ervaring op de achtergrond alle batterij optimalisaties uit, die de achtergrondactiviteit van Immich beperkt.\n\nAangezien dit apparaatspecifiek is, zoek de vereiste informatie op voor de fabrikant van je apparaat.",
|
||||
"backup_controller_page_background_battery_info_ok": "OK",
|
||||
@@ -87,11 +92,21 @@
|
||||
"cache_settings_subtitle": "Beheer het cachegedrag van de Immich app",
|
||||
"cache_settings_thumbnail_size": "Thumbnail cachegrootte ({} items)",
|
||||
"cache_settings_title": "Cache instellingen",
|
||||
"change_password_form_confirm_password": "Bevestig wachtwoord",
|
||||
"change_password_form_description": "Hallo {firstName} {lastName},\n\nDit is ofwel de eerste keer dat je inlogd of er is een verzoek gedaan om je wachtwoord te wijzigen. Vul hieronder een nieuw wachtwoord in.",
|
||||
"change_password_form_new_password": "Nieuw wachtwoord",
|
||||
"change_password_form_password_mismatch": "Wachtwoorden komen niet overeen",
|
||||
"change_password_form_reenter_new_password": "Vul het wachtwoord opnieuw in",
|
||||
"common_add_to_album": "Toevoegen aan album",
|
||||
"common_change_password": "Wachtwoord wijzigen",
|
||||
"common_create_new_album": "Maak nieuw album",
|
||||
"common_shared": "Gedeeld",
|
||||
"control_bottom_app_bar_add_to_album": "Toevoegen aan album",
|
||||
"control_bottom_app_bar_album_info": "{} items",
|
||||
"control_bottom_app_bar_album_info_shared": "{} items · Gedeeld",
|
||||
"control_bottom_app_bar_create_new_album": "Maak nieuw album",
|
||||
"control_bottom_app_bar_delete": "Verwijderen",
|
||||
"control_bottom_app_bar_favorite": "Favoriet",
|
||||
"control_bottom_app_bar_share": "Delen",
|
||||
"create_album_page_untitled": "Naamloos",
|
||||
"create_shared_album_page_create": "Aanmaken",
|
||||
@@ -114,9 +129,13 @@
|
||||
"experimental_settings_title": "Experimenteel",
|
||||
"favorites_page_title": "Favorieten",
|
||||
"home_page_add_to_album_conflicts": "{added} items toegevoegd aan album {album}. {failed} items staan al in het album.",
|
||||
"home_page_add_to_album_err_local": "Lokale items kunnen nog niet aan albums worden toegevoegd, overslaan",
|
||||
"home_page_add_to_album_success": "{added} items toegevoegd aan album {album}.",
|
||||
"home_page_building_timeline": "Tijdlijn opbouwen",
|
||||
"home_page_favorite_err_local": "Lokale items kunnen nog niet als favoriet worden aangemerkt, overslaan",
|
||||
"home_page_first_time_notice": "Als dit de eerste keer is dat je de app gebruikt, zorg er dan voor dat je een back-up album kiest, zodat de tijdlijn gevuld kan worden met foto's en video's uit het album.",
|
||||
"image_viewer_page_state_provider_download_error": "Download mislukt",
|
||||
"image_viewer_page_state_provider_download_success": "Download succesvol",
|
||||
"library_page_albums": "Albums",
|
||||
"library_page_favorites": "Favorieten",
|
||||
"library_page_new_album": "Nieuw album",
|
||||
@@ -140,8 +159,14 @@
|
||||
"login_form_password_hint": "wachtwoord",
|
||||
"login_form_save_login": "Ingelogd blijven",
|
||||
"monthly_title_text_date_format": "MMMM y",
|
||||
"notification_permission_dialog_cancel": "Annuleren",
|
||||
"notification_permission_dialog_content": "Om meldingen in te schakelen, ga naar Instellingen en selecteer toestaan.",
|
||||
"notification_permission_dialog_settings": "Instellingen",
|
||||
"notification_permission_list_tile_content": "Geef toestemming om meldingen in te schakelen.",
|
||||
"notification_permission_list_tile_enable_button": "Meldingen inschakelen",
|
||||
"notification_permission_list_tile_title": "Toestemming meldingen",
|
||||
"profile_drawer_app_logs": "Logboek",
|
||||
"profile_drawer_client_server_up_to_date": "Client en server zijn up-to-date",
|
||||
"profile_drawer_client_server_up_to_date": "App en server zijn up-to-date",
|
||||
"profile_drawer_settings": "Instellingen",
|
||||
"profile_drawer_sign_out": "Uitloggen",
|
||||
"search_bar_hint": "Zoeken naar foto's",
|
||||
@@ -153,6 +178,8 @@
|
||||
"select_additional_user_for_sharing_page_suggestions": "Suggesties",
|
||||
"select_user_for_sharing_page_err_album": "Album aanmaken mislukt",
|
||||
"select_user_for_sharing_page_share_suggestions": "Suggesties",
|
||||
"server_info_box_app_version": "App versie",
|
||||
"server_info_box_server_version": "Server versie",
|
||||
"setting_image_viewer_help": "De gedetailleerde weergave laadt eerst de kleine thumbnail, vervolgens het middelgrote voorbeeld (indien ingeschakeld) en ten slotte het origineel (indien ingeschakeld).",
|
||||
"setting_image_viewer_original_subtitle": "Inschakelen om de originele afbeelding met volledige resolutie (groot!) te laden. Uitschakelen om datagebruik te verminderen (zowel netwerk- als apparaatcache).",
|
||||
"setting_image_viewer_original_title": "Originele afbeelding laden",
|
||||
|
||||
233
mobile/assets/i18n/no-NO.json
Normal file
@@ -0,0 +1,233 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Lagt til i {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Allerede i {album}",
|
||||
"album_info_card_backup_album_excluded": "EKSKLUDERT",
|
||||
"album_info_card_backup_album_included": "INKLUDERT",
|
||||
"album_thumbnail_card_item": "1 objekt",
|
||||
"album_thumbnail_card_items": "{} objekter",
|
||||
"album_thumbnail_card_shared": "Delt",
|
||||
"album_viewer_appbar_share_delete": "Slett album",
|
||||
"album_viewer_appbar_share_err_delete": "Feilet ved sletting av album",
|
||||
"album_viewer_appbar_share_err_leave": "Kunne ikke forlate albumet",
|
||||
"album_viewer_appbar_share_err_remove": "Det oppstod ett problem ved fjerning av objekter fra albumet",
|
||||
"album_viewer_appbar_share_err_title": "Feilet ved endring av albumtittel",
|
||||
"album_viewer_appbar_share_leave": "Forlat album",
|
||||
"album_viewer_appbar_share_remove": "Fjern fra album",
|
||||
"album_viewer_page_share_add_users": "Legg til brukere",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamisk layout",
|
||||
"asset_list_layout_settings_group_by": "Grupper bilder etter",
|
||||
"asset_list_layout_settings_group_by_month": "Måned",
|
||||
"asset_list_layout_settings_group_by_month_day": "Måned + dag",
|
||||
"asset_list_settings_subtitle": "Innstillinger for layout for fotorutenett",
|
||||
"asset_list_settings_title": "Fotorutenett",
|
||||
"backup_album_selection_page_albums_device": "Albumer på enhet ({})",
|
||||
"backup_album_selection_page_albums_tap": "Trykk for å inkludere, dobbelttrykk for å ekskludere",
|
||||
"backup_album_selection_page_assets_scatter": "Objekter kan eksistere i flere album. Men album kan kun bli inkludert eller ekskludert under backup-prosessen.",
|
||||
"backup_album_selection_page_select_albums": "Velg album",
|
||||
"backup_album_selection_page_selection_info": "Valginfo",
|
||||
"backup_album_selection_page_total_assets": "Totalt unike objekter",
|
||||
"backup_all": "Alle",
|
||||
"backup_background_service_backup_failed_message": "Feilet ved backup av objekter. Prøver på nytt...",
|
||||
"backup_background_service_connection_failed_message": "Feilet ved tilkobling til server. Prøver på nytt...",
|
||||
"backup_background_service_current_upload_notification": "Laster opp {}",
|
||||
"backup_background_service_default_notification": "Sjekker for nye objekter...",
|
||||
"backup_background_service_error_title": "Backup feil",
|
||||
"backup_background_service_in_progress_notification": "Foretar backup av objekter...",
|
||||
"backup_background_service_upload_failure_notification": "Filet under opplasting {}",
|
||||
"backup_controller_page_albums": "Sikkerhetskopier albumer",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "Hvis meg hvordan",
|
||||
"backup_controller_page_background_battery_info_message": "For den beste bakgrunnsbackup opplevelsen, deaktiver enhver batterioptimalisering som begrenser bakgrunnsaktiviteten til Immich.\n\nSiden dette er en enhets-spesifik justering, se innstillinger for den aktuelle enhet.",
|
||||
"backup_controller_page_background_battery_info_ok": "OK",
|
||||
"backup_controller_page_background_battery_info_title": "Batterioptimalisering",
|
||||
"backup_controller_page_background_charging": "Kun ved lading",
|
||||
"backup_controller_page_background_configure_error": "Feilet under konfigurering av bakgrunnstjenesten",
|
||||
"backup_controller_page_background_delay": "Forsink backup av nye objekter: {}",
|
||||
"backup_controller_page_background_description": "Skru på bakgrunnstjenesten for å automatisk ta backup av alle nye objekter uten å måtte åpne appen",
|
||||
"backup_controller_page_background_is_off": "Automatisk bakgrunnsbackup er deaktivert",
|
||||
"backup_controller_page_background_is_on": "Automatisk bakgrunnsbackup er aktivert",
|
||||
"backup_controller_page_background_turn_off": "Skru av bakgrunnstjenesten",
|
||||
"backup_controller_page_background_turn_on": "Skru på bakgrunnstjenesten",
|
||||
"backup_controller_page_background_wifi": "Kun på WiFi",
|
||||
"backup_controller_page_backup": "Sikkerhetskopier",
|
||||
"backup_controller_page_backup_selected": "Valgte:",
|
||||
"backup_controller_page_backup_sub": "Opplastede bilder og videoer",
|
||||
"backup_controller_page_cancel": "Avbryt",
|
||||
"backup_controller_page_created": "Opprettet den: {}",
|
||||
"backup_controller_page_desc_backup": "Slå på sikkerhetskopiering i forgrunnen for automatisk å laste opp nye objekter til serveren når du åpner appen.",
|
||||
"backup_controller_page_excluded": "Ekskludert:",
|
||||
"backup_controller_page_failed": "Mislyktes ({})",
|
||||
"backup_controller_page_filename": "Filnavn: {} [{}]",
|
||||
"backup_controller_page_id": "ID: {}",
|
||||
"backup_controller_page_info": "Sikkerhetskopi informasjon",
|
||||
"backup_controller_page_none_selected": "Ingen valgt",
|
||||
"backup_controller_page_remainder": "Gjenstår",
|
||||
"backup_controller_page_remainder_sub": "Gjenstående bilder og videoer å laste opp fra utvalg",
|
||||
"backup_controller_page_select": "Velg",
|
||||
"backup_controller_page_server_storage": "Serverlagring",
|
||||
"backup_controller_page_start_backup": "Start backup",
|
||||
"backup_controller_page_status_off": "Automatisk sikkerhetskopiering i forgrunnen er av",
|
||||
"backup_controller_page_status_on": "Automatisk sikkerhetskopiering i forgrunnen er på",
|
||||
"backup_controller_page_storage_format": "{} av {} brukt",
|
||||
"backup_controller_page_to_backup": "Albumer som skal sikkerhetskopieres",
|
||||
"backup_controller_page_total": "Totalt",
|
||||
"backup_controller_page_total_sub": "Alle unike bilder og videoer fra valgte album",
|
||||
"backup_controller_page_turn_off": "Slå av sikkerhetskopiering i forgrunnen",
|
||||
"backup_controller_page_turn_on": "Slå på sikkerhetskopiering i forgrunnen",
|
||||
"backup_controller_page_uploading_file_info": "Filinformasjon på opplastende fil",
|
||||
"backup_err_only_album": "Kan ikke fjerne det eneste albumet",
|
||||
"backup_info_card_assets": "objekter",
|
||||
"cache_settings_album_thumbnails": "Bibliotekside miniatyrbilder ({} objekter)",
|
||||
"cache_settings_clear_cache_button": "Tøm buffer",
|
||||
"cache_settings_clear_cache_button_title": "Tømmer app'ens buffer. Dette vil ha betydelig innvirkning på appens ytelse inntil buffer er gjenoppbygd.",
|
||||
"cache_settings_image_cache_size": "Bilde bufferstørrelse ({} objekter)",
|
||||
"cache_settings_statistics_album": "Bibliotek miniatyrbilder",
|
||||
"cache_settings_statistics_assets": "{} objekter ({})",
|
||||
"cache_settings_statistics_full": "Originalbilder",
|
||||
"cache_settings_statistics_shared": "Delte album miniatyrbilder",
|
||||
"cache_settings_statistics_thumbnail": "Miniatyrbilder",
|
||||
"cache_settings_statistics_title": "Bufferbruk",
|
||||
"cache_settings_subtitle": "Kontroller bufringsadferden til Immich-mobilapplikasjonen",
|
||||
"cache_settings_thumbnail_size": "MIniatyrbilder bufferstørrelse ({} objekter)",
|
||||
"cache_settings_title": "Bufringsinnstillinger",
|
||||
"change_password_form_confirm_password": "Bekreft passord",
|
||||
"change_password_form_description": "Hei {firstName} {lastName}!\n\nDette er enten første gang du logger på systemet, eller det er sendt en forespørsel om å endre passordet ditt. Vennligst skriv inn det nye passordet nedenfor.",
|
||||
"change_password_form_new_password": "Nytt passord",
|
||||
"change_password_form_password_mismatch": "Passordene stemmer ikke",
|
||||
"change_password_form_reenter_new_password": "Skriv nytt passord igjen",
|
||||
"common_add_to_album": "Legg til i album",
|
||||
"common_change_password": "Endre passord",
|
||||
"common_create_new_album": "Lag nytt album",
|
||||
"common_shared": "Delt",
|
||||
"control_bottom_app_bar_add_to_album": "Legg til i album",
|
||||
"control_bottom_app_bar_album_info": "{} objekter",
|
||||
"control_bottom_app_bar_album_info_shared": "{} objekter · Delt",
|
||||
"control_bottom_app_bar_create_new_album": "Lag nytt album",
|
||||
"control_bottom_app_bar_delete": "Slett",
|
||||
"control_bottom_app_bar_favorite": "Favoritt",
|
||||
"control_bottom_app_bar_share": "Del",
|
||||
"create_album_page_untitled": "Uten navn",
|
||||
"create_shared_album_page_create": "Opprett",
|
||||
"create_shared_album_page_share": "Del",
|
||||
"create_shared_album_page_share_add_assets": "LEGG TIL OBJEKTER",
|
||||
"create_shared_album_page_share_select_photos": "Velg bilder",
|
||||
"daily_title_text_date": "E, MMM dd",
|
||||
"daily_title_text_date_year": "E, MMM dd, yyyy",
|
||||
"date_format": "E, LLL d, y • h:mm a",
|
||||
"delete_dialog_alert": "Disse objektene vil bli slettet permanent fra Immich og fra enheten",
|
||||
"delete_dialog_cancel": "Avbryt",
|
||||
"delete_dialog_ok": "Slett",
|
||||
"delete_dialog_title": "Slett permanent",
|
||||
"exif_bottom_sheet_description": "Legg til beskrivelse...",
|
||||
"exif_bottom_sheet_details": "DETALJER",
|
||||
"exif_bottom_sheet_location": "PLASSERING",
|
||||
"experimental_settings_new_asset_list_subtitle": "Under utvikling",
|
||||
"experimental_settings_new_asset_list_title": "Aktiver eksperimentell grid-visning",
|
||||
"experimental_settings_subtitle": "Bruk på egen risiko!",
|
||||
"experimental_settings_title": "Eksperimentelt",
|
||||
"favorites_page_title": "Favoritter",
|
||||
"home_page_add_to_album_conflicts": "Lagt til {added} objekter til album {album}. {failed} objekter er allerede i albumet.",
|
||||
"home_page_add_to_album_err_local": "Kan ikke legge til lokale objekter til album enda, hopper over",
|
||||
"home_page_add_to_album_success": "Lagt til {added} objekter til album {album}.",
|
||||
"home_page_building_timeline": "Genererer tidslinjen",
|
||||
"home_page_favorite_err_local": "Kan ikke sette favoritt på lokale objekter enda, hopper over",
|
||||
"home_page_first_time_notice": "Hvis dette er første gangen du benytter appen, så velg ett album(eller flere) slik at tidslinjen kan genereres med dine bilder og videoer.",
|
||||
"image_viewer_page_state_provider_download_error": "Nedlasting feilet",
|
||||
"image_viewer_page_state_provider_download_success": "Nedlasting vellykket",
|
||||
"library_page_albums": "Albumer",
|
||||
"library_page_favorites": "Favoritter",
|
||||
"library_page_new_album": "Nytt album",
|
||||
"library_page_sharing": "Deling",
|
||||
"library_page_sort_created": "Nylig opplastet",
|
||||
"library_page_sort_title": "Album tittel",
|
||||
"login_form_button_text": "Logg inn",
|
||||
"login_form_email_hint": "dinepost@epost.no",
|
||||
"login_form_endpoint_hint": "http://din-server-ip:port/api",
|
||||
"login_form_endpoint_url": "Serverendepunkt-URL",
|
||||
"login_form_err_http": "Vennligst spesifiser http:// eller https://",
|
||||
"login_form_err_invalid_email": "Ugyldig Epostadresse",
|
||||
"login_form_err_invalid_url": "Ugyldig URL",
|
||||
"login_form_err_leading_whitespace": "Ledende mellomrom",
|
||||
"login_form_err_trailing_whitespace": "Etterfølgende mellomrom",
|
||||
"login_form_failed_get_oauth_server_config": "Feil innlogging ved bruk av OAuth, sjekk server URL",
|
||||
"login_form_failed_get_oauth_server_disable": "OAuth innlogging er ikke tilgjengelig på denne serveren",
|
||||
"login_form_failed_login": "Feil ved innlogging, sjekk server URL, epost og passord",
|
||||
"login_form_label_email": "Epostadresse",
|
||||
"login_form_label_password": "Passord",
|
||||
"login_form_password_hint": "passord",
|
||||
"login_form_save_login": "Forbli innlogget",
|
||||
"monthly_title_text_date_format": "MMMM y",
|
||||
"notification_permission_dialog_cancel": "Avbryt",
|
||||
"notification_permission_dialog_content": "For å aktivere notifikasjoner, gå til Innstillinger og velg tillat.",
|
||||
"notification_permission_dialog_settings": "Innstillinger",
|
||||
"notification_permission_list_tile_content": "Tillat tilgang for å aktivere notifikasjoner",
|
||||
"notification_permission_list_tile_enable_button": "Aktiver notifikasjoner",
|
||||
"notification_permission_list_tile_title": "Notifikasjonstilgang",
|
||||
"profile_drawer_app_logs": "Logg",
|
||||
"profile_drawer_client_server_up_to_date": "Klient og Server er oppdatert",
|
||||
"profile_drawer_settings": "Innstillinger",
|
||||
"profile_drawer_sign_out": "Logg ut",
|
||||
"search_bar_hint": "Søk i dine bilder",
|
||||
"search_page_no_objects": "Ingen objektinfo tilgjengelig",
|
||||
"search_page_no_places": "Ingen plasseringsinfo tilgjengelig",
|
||||
"search_page_places": "Plasser",
|
||||
"search_page_things": "Ting",
|
||||
"search_result_page_new_search_hint": "Nytt søk",
|
||||
"select_additional_user_for_sharing_page_suggestions": "Forslag",
|
||||
"select_user_for_sharing_page_err_album": "Feilet ved oppretting av album",
|
||||
"select_user_for_sharing_page_share_suggestions": "Forslag",
|
||||
"server_info_box_app_version": "App versjon",
|
||||
"server_info_box_server_version": "Server versjon",
|
||||
"setting_image_viewer_help": "Først lastes mikrobilder, deretter middels-oppløsningbildet (hvis aktivert), til slutt lastes original (hvis aktivert).",
|
||||
"setting_image_viewer_original_subtitle": "Aktiver for å laste originalbildet i full oppløsning (Stort!). Deaktiver for å spare databruk (både nettverksbruk og bufferdata på enheten).",
|
||||
"setting_image_viewer_original_title": "Last originalbildet",
|
||||
"setting_image_viewer_preview_subtitle": "Aktiver for å laste ett bilde av middels-oppløsning. Deaktiver for å enten direkte laste inn originalen eller kun benytte miniatyrbilde.",
|
||||
"setting_image_viewer_preview_title": "Last forhåndsvisningsbilde",
|
||||
"setting_notifications_notify_failures_grace_period": "Varsle om sikkerhetskopieringsfeil i bakgrunnen: {}",
|
||||
"setting_notifications_notify_hours": "{} timer",
|
||||
"setting_notifications_notify_immediately": "umiddelbart",
|
||||
"setting_notifications_notify_minutes": "{} minutter",
|
||||
"setting_notifications_notify_never": "aldri",
|
||||
"setting_notifications_notify_seconds": "{} sekunder",
|
||||
"setting_notifications_single_progress_subtitle": "Detaljert opplastingsinformasjon pr objekt",
|
||||
"setting_notifications_single_progress_title": "Vis detaljert status på bakgrunnsbackup",
|
||||
"setting_notifications_subtitle": "Juster notifikasjonsinnstillinger",
|
||||
"setting_notifications_title": "Notifikasjoner",
|
||||
"setting_notifications_total_progress_subtitle": "Total opplastingsstatus (fullført/totalt objekter)",
|
||||
"setting_notifications_total_progress_title": "Vis status på bakgrunnsbackup",
|
||||
"setting_pages_app_bar_settings": "Innstillinger",
|
||||
"settings_require_restart": "Vennligst restart Immich for å aktivere denne innstillingen",
|
||||
"share_add": "Legg til",
|
||||
"share_add_photos": "Legg til bilder",
|
||||
"share_add_title": "Legg til tittel",
|
||||
"share_create_album": "Opprett album",
|
||||
"share_dialog_preparing": "Forbereder...",
|
||||
"share_invite": "Inviter til album",
|
||||
"sharing_page_album": "Delte album",
|
||||
"sharing_page_description": "Lag delte album for å dele bilder og videoer med folk i nettverket ditt.",
|
||||
"sharing_page_empty_list": "TOM LISTE",
|
||||
"sharing_silver_appbar_create_shared_album": "Lag delt album",
|
||||
"sharing_silver_appbar_share_partner": "Del med partner",
|
||||
"tab_controller_nav_library": "Bibliotek",
|
||||
"tab_controller_nav_photos": "Bilder",
|
||||
"tab_controller_nav_search": "Søk",
|
||||
"tab_controller_nav_sharing": "Deling",
|
||||
"theme_setting_asset_list_storage_indicator_title": "Vis lagringsindiaktor på objektfliser",
|
||||
"theme_setting_asset_list_tiles_per_row_title": "Antall ressurser per rad ({})",
|
||||
"theme_setting_dark_mode_switch": "Mørk modus",
|
||||
"theme_setting_image_viewer_quality_subtitle": "Juster kvaliteten på detaljer med bildeviser",
|
||||
"theme_setting_image_viewer_quality_title": "Bilderviser-kvalitet",
|
||||
"theme_setting_system_theme_switch": "Automatisk (følg system)",
|
||||
"theme_setting_theme_subtitle": "Velg app'ens temainnstilling",
|
||||
"theme_setting_theme_title": "Tema",
|
||||
"theme_setting_three_stage_loading_subtitle": "Tre-trinns lasting kan øke lasteytelsen, men forårsaker betydelig høyere nettverksbelastning",
|
||||
"theme_setting_three_stage_loading_title": "Aktiver 3-stegs innlasting",
|
||||
"version_announcement_overlay_ack": "Bekreft",
|
||||
"version_announcement_overlay_release_notes": "Endringslogg",
|
||||
"version_announcement_overlay_text_1": "Hei, det er en ny versjon av",
|
||||
"version_announcement_overlay_text_2": "vennligst ta deg tid til å besøke",
|
||||
"version_announcement_overlay_text_3": "og verifiser at din docker-compose og .env oppsett er oppdatert for å forhindre en eventuell miskonfigurasjon. Spesielt hvis du benytter WatchTower eller en annen tjeneste som håndterer oppdatering av applikasjoner på serveren automatisk.",
|
||||
"version_announcement_overlay_title": "Ny serverversjon tilgjengelig"
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
||||
"album_info_card_backup_album_excluded": "WYKLUCZONE",
|
||||
"album_info_card_backup_album_included": "WŁĄCZONE",
|
||||
"album_thumbnail_card_item": "1 pozycja",
|
||||
@@ -33,6 +35,9 @@
|
||||
"backup_background_service_in_progress_notification": "Tworzę kopię twoich zasobów...",
|
||||
"backup_background_service_upload_failure_notification": "Nie udało się przesłać {}",
|
||||
"backup_controller_page_albums": "Backup Albumów",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "Pokaż mi jak",
|
||||
"backup_controller_page_background_battery_info_message": "Aby uzyskać najlepsze rezultaty podczas tworzenia kopii zapasowej w tle, należy wyłączyć wszelkie optymalizacje baterii ograniczające aktywność w tle dla Immich w urządzeniu.\n\nPonieważ jest to zależne od urządzenia, proszę sprawdzić wymagane informacje dla producenta urządzenia.",
|
||||
"backup_controller_page_background_battery_info_ok": "OK",
|
||||
@@ -87,11 +92,21 @@
|
||||
"cache_settings_subtitle": "Kontrolowanie zachowania buforowania aplikacji mobilnej Immich",
|
||||
"cache_settings_thumbnail_size": "Rozmiar pamięci podręcznej miniatur ({} zasobów)",
|
||||
"cache_settings_title": "Ustawienia Buforowania",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
"common_add_to_album": "Add to album",
|
||||
"common_change_password": "Change Password",
|
||||
"common_create_new_album": "Create new album",
|
||||
"common_shared": "Shared",
|
||||
"control_bottom_app_bar_add_to_album": "Add to album",
|
||||
"control_bottom_app_bar_album_info": "{} items",
|
||||
"control_bottom_app_bar_album_info_shared": "{} items · Shared",
|
||||
"control_bottom_app_bar_create_new_album": "Create new album",
|
||||
"control_bottom_app_bar_delete": "Usuń",
|
||||
"control_bottom_app_bar_favorite": "Favorite",
|
||||
"control_bottom_app_bar_share": "Udostępnij",
|
||||
"create_album_page_untitled": "Bez tytułu",
|
||||
"create_shared_album_page_create": "Utwórz",
|
||||
@@ -114,9 +129,13 @@
|
||||
"experimental_settings_title": "Experimental",
|
||||
"favorites_page_title": "Favorites",
|
||||
"home_page_add_to_album_conflicts": "Added {added} assets to album {album}. {failed} assets are already in the album.",
|
||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
||||
"home_page_add_to_album_success": "Added {added} assets to album {album}.",
|
||||
"home_page_building_timeline": "Building the timeline",
|
||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
||||
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
|
||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
||||
"library_page_albums": "Albumy",
|
||||
"library_page_favorites": "Favorites",
|
||||
"library_page_new_album": "Nowy album",
|
||||
@@ -140,6 +159,12 @@
|
||||
"login_form_password_hint": "hasło",
|
||||
"login_form_save_login": "Pozostań zalogowany",
|
||||
"monthly_title_text_date_format": "MMMM y",
|
||||
"notification_permission_dialog_cancel": "Cancel",
|
||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
||||
"notification_permission_dialog_settings": "Settings",
|
||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
||||
"notification_permission_list_tile_title": "Notification Permission",
|
||||
"profile_drawer_app_logs": "Logs",
|
||||
"profile_drawer_client_server_up_to_date": "Klient i serwer są aktualne",
|
||||
"profile_drawer_settings": "Ustawienia",
|
||||
@@ -153,6 +178,8 @@
|
||||
"select_additional_user_for_sharing_page_suggestions": "Propozycje",
|
||||
"select_user_for_sharing_page_err_album": "Nie udało się utworzyć albumu",
|
||||
"select_user_for_sharing_page_share_suggestions": "Propozycje",
|
||||
"server_info_box_app_version": "App Version",
|
||||
"server_info_box_server_version": "Server Version",
|
||||
"setting_image_viewer_help": "The detail viewer loads the small thumbnail first, then loads the medium-size preview (if enabled), finally loads the original (if enabled).",
|
||||
"setting_image_viewer_original_subtitle": "Enable to load the original full-resolution image (large!). Disable to reduce data usage (both network and on device cache).",
|
||||
"setting_image_viewer_original_title": "Load original image",
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Добавлено в {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Уже в {album}",
|
||||
"album_info_card_backup_album_excluded": "ИСКЛЮЧЕН",
|
||||
"album_info_card_backup_album_included": "ВКЛЮЧЕН",
|
||||
"album_thumbnail_card_item": "1 объект",
|
||||
@@ -12,10 +14,10 @@
|
||||
"album_viewer_appbar_share_leave": "Покинуть альбом",
|
||||
"album_viewer_appbar_share_remove": "Удалить из альбома",
|
||||
"album_viewer_page_share_add_users": "Добавить пользователей",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamic layout",
|
||||
"asset_list_layout_settings_group_by": "Group assets by",
|
||||
"asset_list_layout_settings_group_by_month": "Month",
|
||||
"asset_list_layout_settings_group_by_month_day": "Month + day",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Динамическое расположение",
|
||||
"asset_list_layout_settings_group_by": "Группировать объекты по",
|
||||
"asset_list_layout_settings_group_by_month": "месяцу",
|
||||
"asset_list_layout_settings_group_by_month_day": "месяцу и дню",
|
||||
"asset_list_settings_subtitle": "Настройки макета сетки фотографий",
|
||||
"asset_list_settings_title": "Сетка фотографий",
|
||||
"backup_album_selection_page_albums_device": "Альбомов на устройстве ({})",
|
||||
@@ -33,6 +35,9 @@
|
||||
"backup_background_service_in_progress_notification": "Резервное копирование ваших объектов…",
|
||||
"backup_background_service_upload_failure_notification": "Ошибка загрузки {}",
|
||||
"backup_controller_page_albums": "Резервное копирование альбомов",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "Показать как",
|
||||
"backup_controller_page_background_battery_info_message": "Для наилучшего фонового резервного копирования отключите любые настройки оптимизации батареи, ограничивающие фоновую активность для Immich.\n\nПоскольку это зависит от устройства, найдите необходимую информацию для производителя вашего устройства.",
|
||||
"backup_controller_page_background_battery_info_ok": "ОК",
|
||||
@@ -87,20 +92,30 @@
|
||||
"cache_settings_subtitle": "Управление кэшированием мобильного приложения Immich",
|
||||
"cache_settings_thumbnail_size": "Размер кэша эскизов ({} объектов)",
|
||||
"cache_settings_title": "Настройки кэширования",
|
||||
"change_password_form_confirm_password": "Подтвердите пароль",
|
||||
"change_password_form_description": "Привет {firstName} {lastName},\n\nЭто либо ваш первый вход в систему, либо был сделан запрос на смену пароля. Пожалуйста, введите новый пароль ниже.",
|
||||
"change_password_form_new_password": "Новый пароль",
|
||||
"change_password_form_password_mismatch": "Пароли не совпадают",
|
||||
"change_password_form_reenter_new_password": "Повторно введите новый пароль",
|
||||
"common_add_to_album": "Добавить в альбом",
|
||||
"common_change_password": "Изменить пароль",
|
||||
"common_create_new_album": "Создать новый альбом",
|
||||
"common_shared": "Общие",
|
||||
"control_bottom_app_bar_add_to_album": "Добавить в альбом",
|
||||
"control_bottom_app_bar_album_info": "{} файлов",
|
||||
"control_bottom_app_bar_album_info_shared": "{} файлов · Общий",
|
||||
"control_bottom_app_bar_create_new_album": "\nСоздать новый альбом",
|
||||
"control_bottom_app_bar_delete": "Удалить",
|
||||
"control_bottom_app_bar_favorite": "Избранное",
|
||||
"control_bottom_app_bar_share": "Поделиться",
|
||||
"create_album_page_untitled": "Без названия",
|
||||
"create_shared_album_page_create": "Создать",
|
||||
"create_shared_album_page_share": "Поделиться",
|
||||
"create_shared_album_page_share_add_assets": "ДОБАВИТЬ ОБЪЕКТЫ",
|
||||
"create_shared_album_page_share_select_photos": "Выберите фотографии",
|
||||
"daily_title_text_date": "ДН, МММ дд",
|
||||
"daily_title_text_date_year": "ДН, МММ дд, гггг",
|
||||
"date_format": "ДН, LLL д, г • ч:мм a",
|
||||
"daily_title_text_date": "E, MMM dd",
|
||||
"daily_title_text_date_year": "E, MMM dd, yyyy",
|
||||
"date_format": "E, LLL d, y • h:mm a",
|
||||
"delete_dialog_alert": "Эти объекты будут безвозвратно удалены из приложения, а также с вашего устройства",
|
||||
"delete_dialog_cancel": "Отменить",
|
||||
"delete_dialog_ok": "Удалить",
|
||||
@@ -114,9 +129,13 @@
|
||||
"experimental_settings_title": "Экспериментальные функции",
|
||||
"favorites_page_title": "Избранное",
|
||||
"home_page_add_to_album_conflicts": "Добавлено {added} объектов в альбом {album}. Объекты {failed} уже есть в альбоме.",
|
||||
"home_page_add_to_album_err_local": "Пока нельзя добавлять локальные объекты в альбомы, пропускаем",
|
||||
"home_page_add_to_album_success": "Добавлено {added} объектов в альбом {album}.",
|
||||
"home_page_building_timeline": "Построение временной шкалы",
|
||||
"home_page_favorite_err_local": "Пока не удается добавить в избранное локальные объекты, пропускаем",
|
||||
"home_page_first_time_notice": "Если вы используете приложение впервые, убедитесь, что вы выбрали резервный(е) альбом(ы), чтобы временная шкала могла заполнить фотографии и видео в альбоме(ах).",
|
||||
"image_viewer_page_state_provider_download_error": "Ошибка загрузки",
|
||||
"image_viewer_page_state_provider_download_success": "Успешно загружено",
|
||||
"library_page_albums": "Альбомы",
|
||||
"library_page_favorites": "Избранное",
|
||||
"library_page_new_album": "Новый альбом",
|
||||
@@ -139,7 +158,13 @@
|
||||
"login_form_label_password": "Пароль",
|
||||
"login_form_password_hint": "пароль",
|
||||
"login_form_save_login": "Оставаться в системе",
|
||||
"monthly_title_text_date_format": "ММММ г",
|
||||
"monthly_title_text_date_format": "MMMM y",
|
||||
"notification_permission_dialog_cancel": "Отмена",
|
||||
"notification_permission_dialog_content": "Чтобы включить уведомления, перейдите в «Настройки» и выберите «Разрешить».",
|
||||
"notification_permission_dialog_settings": "Настройки",
|
||||
"notification_permission_list_tile_content": "Предоставьте разрешение на включение уведомлений",
|
||||
"notification_permission_list_tile_enable_button": "Включить уведомления",
|
||||
"notification_permission_list_tile_title": "Разрешение на уведомление",
|
||||
"profile_drawer_app_logs": "Журналы",
|
||||
"profile_drawer_client_server_up_to_date": "Клиент и сервер обновлены",
|
||||
"profile_drawer_settings": "Настройки",
|
||||
@@ -153,6 +178,8 @@
|
||||
"select_additional_user_for_sharing_page_suggestions": "Предложения",
|
||||
"select_user_for_sharing_page_err_album": "\nНе удалось создать альбом",
|
||||
"select_user_for_sharing_page_share_suggestions": "Предложения",
|
||||
"server_info_box_app_version": "Версия приложения",
|
||||
"server_info_box_server_version": "Версия сервера",
|
||||
"setting_image_viewer_help": "Средство просмотра деталей сначала загружает маленькую миниатюру, затем загружает предварительный просмотр среднего размера (если включено) и, наконец, загружает оригинал (если включено).",
|
||||
"setting_image_viewer_original_subtitle": "Включите загрузку оригинального изображения в полном разрешении (большое!). Отключите, чтобы уменьшить объем данных (как в сети, так и в кеше устройства).",
|
||||
"setting_image_viewer_original_title": "Загрузить исходное изображение",
|
||||
@@ -200,7 +227,7 @@
|
||||
"version_announcement_overlay_ack": "Подтверждение",
|
||||
"version_announcement_overlay_release_notes": "примечания к выпуску",
|
||||
"version_announcement_overlay_text_1": "Привет друг, вышел новый релиз",
|
||||
"version_announcement_overlay_text_2": "пожалуйста, найдите время, чтобы посетить",
|
||||
"version_announcement_overlay_text_2": "пожалуйста, найдите время, чтобы посетить ",
|
||||
"version_announcement_overlay_text_3": " и убедитесь, что ваши настройки docker-compose и .env обновлены, чтобы предотвратить любые неправильные настройки, особенно если вы используете WatchTower или любой другой механизм, который обрабатывает обновление вашего серверного приложения автоматически.",
|
||||
"version_announcement_overlay_title": "Доступна новая версия сервера \uD83C\uDF89"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Pridané do {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Už v {album}",
|
||||
"album_info_card_backup_album_excluded": "VYLÚČENÉ",
|
||||
"album_info_card_backup_album_included": "ZAHRNUTÉ",
|
||||
"album_thumbnail_card_item": "1 položka",
|
||||
@@ -12,10 +14,10 @@
|
||||
"album_viewer_appbar_share_leave": "Opustiť album",
|
||||
"album_viewer_appbar_share_remove": "Odstrániť z albumu",
|
||||
"album_viewer_page_share_add_users": "Pridať používateľov",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamic layout",
|
||||
"asset_list_layout_settings_group_by": "Group assets by",
|
||||
"asset_list_layout_settings_group_by_month": "Month",
|
||||
"asset_list_layout_settings_group_by_month_day": "Month + day",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamické rozloženie",
|
||||
"asset_list_layout_settings_group_by": "Zoskupiť položky podľa",
|
||||
"asset_list_layout_settings_group_by_month": "Mesiac",
|
||||
"asset_list_layout_settings_group_by_month_day": "Mesiac + deň",
|
||||
"asset_list_settings_subtitle": "Nastavenia rozloženia mriežky fotografií",
|
||||
"asset_list_settings_title": "Fotografická mriežka",
|
||||
"backup_album_selection_page_albums_device": "Albumy v zariadení ({})",
|
||||
@@ -25,21 +27,24 @@
|
||||
"backup_album_selection_page_selection_info": "Informácie o výbere",
|
||||
"backup_album_selection_page_total_assets": "Celkový počet jedinečných súborov",
|
||||
"backup_all": "Všetko",
|
||||
"backup_background_service_backup_failed_message": "Zálohovanie zdrojov zlyhalo. Skúšam to znova...",
|
||||
"backup_background_service_backup_failed_message": "Zálohovanie médií zlyhalo. Skúšam to znova...",
|
||||
"backup_background_service_connection_failed_message": "Nepodarilo sa pripojiť k serveru. Skúšam to znova...",
|
||||
"backup_background_service_current_upload_notification": "Nahrávanie {}",
|
||||
"backup_background_service_default_notification": "Kontrola nových zdrojov {}",
|
||||
"backup_background_service_default_notification": "Kontrola nových médií {}",
|
||||
"backup_background_service_error_title": "Chyba zálohovania",
|
||||
"backup_background_service_in_progress_notification": "Vytváram kópiu vašich zdrojov...",
|
||||
"backup_background_service_in_progress_notification": "Vytváram kópiu vašich médií...",
|
||||
"backup_background_service_upload_failure_notification": "Nepodarilo sa nahrať {}",
|
||||
"backup_controller_page_albums": "Zálohované albumy",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "Ukáž mi ako",
|
||||
"backup_controller_page_background_battery_info_message": "Ak chcete dosiahnuť najlepšie výsledky pri zálohovaní na pozadí, vypnite všetky optimalizácie batérie, ktoré obmedzujú aktivitu na pozadí pre Immich vo vašom zariadení. Keďže to závisí od zariadenia, skontrolujte požadované informácie pre výrobcu vášho zariadenia.",
|
||||
"backup_controller_page_background_battery_info_ok": "OK",
|
||||
"backup_controller_page_background_battery_info_title": "Optimalizácia batérie",
|
||||
"backup_controller_page_background_charging": "Len počas nabíjania",
|
||||
"backup_controller_page_background_configure_error": "Nepodarilo sa nakonfigurovať službu na pozadí",
|
||||
"backup_controller_page_background_delay": "Oneskorenie zálohovania nových zdrojov: {}",
|
||||
"backup_controller_page_background_delay": "Oneskorenie zálohovania nových médií: {}",
|
||||
"backup_controller_page_background_description": "Povoľte službu na pozadí na automatické zálohovanie všetkých nových aktív bez nutnosti otvorenia aplikácie",
|
||||
"backup_controller_page_background_is_off": "Automatické zálohovanie na pozadí je vypnuté",
|
||||
"backup_controller_page_background_is_on": "Automatické zálohovanie na pozadí je zapnuté",
|
||||
@@ -87,11 +92,21 @@
|
||||
"cache_settings_subtitle": "Ovládanie správania mobilnej aplikácie Immich v medzipamäti",
|
||||
"cache_settings_thumbnail_size": "Veľkosť vyrovnávacej pamäte náhľadov (položiek {})",
|
||||
"cache_settings_title": "Nastavenia vyrovnávacej pamäte",
|
||||
"change_password_form_confirm_password": "Potvrďte heslo",
|
||||
"change_password_form_description": "Dobrý deň, {firstName} {lastName},\n\nBuď sa do systému prihlasujete prvýkrát, alebo bola podaná žiadosť o zmenu hesla. Prosím, zadajte nové heslo nižšie.",
|
||||
"change_password_form_new_password": "Nové heslo",
|
||||
"change_password_form_password_mismatch": "Heslá sa nezhodujú",
|
||||
"change_password_form_reenter_new_password": "Znova zadajte nové heslo",
|
||||
"common_add_to_album": "Pridať do albumu",
|
||||
"common_change_password": "Zmeniť heslo",
|
||||
"common_create_new_album": "Vytvoriť nový album",
|
||||
"common_shared": "Zdieľané",
|
||||
"control_bottom_app_bar_add_to_album": "Pridať do albumu",
|
||||
"control_bottom_app_bar_album_info": "{} položky",
|
||||
"control_bottom_app_bar_album_info_shared": "{} položky - zdieľané",
|
||||
"control_bottom_app_bar_create_new_album": "Vytvoriť nový album",
|
||||
"control_bottom_app_bar_delete": "Vymazať",
|
||||
"control_bottom_app_bar_favorite": "Obľúbené",
|
||||
"control_bottom_app_bar_share": "Zdieľať",
|
||||
"create_album_page_untitled": "Bez názvu",
|
||||
"create_shared_album_page_create": "Vytvoriť",
|
||||
@@ -114,9 +129,13 @@
|
||||
"experimental_settings_title": "Experimentálne",
|
||||
"favorites_page_title": "Obľúbené",
|
||||
"home_page_add_to_album_conflicts": "Pridané {added} položky do albumu {album}. {failed} položky sú už v albume.",
|
||||
"home_page_add_to_album_err_local": "Zatiaľ nie je možné pridať lokálne média do albumov, preskakuje sa",
|
||||
"home_page_add_to_album_success": "Pridané {added} položky do albumu {album}.",
|
||||
"home_page_building_timeline": "Vytváranie časovej osi",
|
||||
"home_page_first_time_notice": "Ak aplikáciu používate prvýkrát, nezabudnite si vybrať zálohované albumy, aby sa na časovej osi mohli nachádzať fotografie a videá z vybraných albumoch.",
|
||||
"home_page_favorite_err_local": "Zatiaľ nie je možné zaradiť lokálne média medzi obľúbené, preskakuje sa",
|
||||
"home_page_first_time_notice": "Ak aplikáciu používate prvý krát, nezabudnite si vybrať zálohované albumy, aby sa na časovej osi mohli nachádzať fotografie a videá z vybraných albumoch.",
|
||||
"image_viewer_page_state_provider_download_error": "Chyba sťahovania",
|
||||
"image_viewer_page_state_provider_download_success": "Sťahovanie bolo úspešné",
|
||||
"library_page_albums": "Albumy",
|
||||
"library_page_favorites": "Obľúbené",
|
||||
"library_page_new_album": "Nový album",
|
||||
@@ -129,7 +148,7 @@
|
||||
"login_form_endpoint_url": "URL adresa servera",
|
||||
"login_form_err_http": "Prosím, uveďte http:// alebo https://",
|
||||
"login_form_err_invalid_email": "Neplatný e-mail",
|
||||
"login_form_err_invalid_url": "Neplatná URL",
|
||||
"login_form_err_invalid_url": "Neplatná URL adresa",
|
||||
"login_form_err_leading_whitespace": "Úvodná medzera",
|
||||
"login_form_err_trailing_whitespace": "Koncové medzera",
|
||||
"login_form_failed_get_oauth_server_config": "Chyba prihlásenia pomocou OAuth, skontrolujte adresu URL servera",
|
||||
@@ -140,6 +159,12 @@
|
||||
"login_form_password_hint": "heslo",
|
||||
"login_form_save_login": "Zostať prihlásený",
|
||||
"monthly_title_text_date_format": "LLLL y",
|
||||
"notification_permission_dialog_cancel": "Zrušiť",
|
||||
"notification_permission_dialog_content": "Ak chcete povoliť upozornenia, prejdite do Nastavenia a vyberte možnosť Povoliť.",
|
||||
"notification_permission_dialog_settings": "Nastavenia",
|
||||
"notification_permission_list_tile_content": "Udeľte oprávnenie k aktivácii oznámení.",
|
||||
"notification_permission_list_tile_enable_button": "Povoliť upozornenia",
|
||||
"notification_permission_list_tile_title": "Povolenie oznámení",
|
||||
"profile_drawer_app_logs": "Logy",
|
||||
"profile_drawer_client_server_up_to_date": "Klient a server sú aktuálne",
|
||||
"profile_drawer_settings": "Nastavenia",
|
||||
@@ -153,10 +178,12 @@
|
||||
"select_additional_user_for_sharing_page_suggestions": "Návrhy",
|
||||
"select_user_for_sharing_page_err_album": "Nepodarilo sa vytvoriť album",
|
||||
"select_user_for_sharing_page_share_suggestions": "Návrhy",
|
||||
"setting_image_viewer_help": "V prehliadači detailov sa najprv načíta malá miniatúra, potom sa načíta náhľad strednej veľkosti (ak je povolený) a nakoniec sa načíta originál (ak je povolený).",
|
||||
"setting_image_viewer_original_subtitle": "Umožňuje načítať pôvodný obrázok v plnom rozlíšení (veľký!). Zakázať pre zníženie používania dát (v sieti aj v medzipamäti zariadenia).",
|
||||
"server_info_box_app_version": "Verzia aplikácie",
|
||||
"server_info_box_server_version": "Verzia servera",
|
||||
"setting_image_viewer_help": "Prehliadač detailov najprv načíta malú miniatúru, potom načíta náhľad strednej veľkosti (ak je povolený) a nakoniec načíta originál (ak je povolený).",
|
||||
"setting_image_viewer_original_subtitle": "Povolením umožníte načítať pôvodný obrázok v plnom rozlíšení (veľký!). Zakázaním znížite používania dát (v sieti, aj v dočasnej pamäte zariadenia).",
|
||||
"setting_image_viewer_original_title": "Načítať pôvodný obrázok",
|
||||
"setting_image_viewer_preview_subtitle": "Umožňuje načítať obrázok so stredným rozlíšením. Zakážte, ak chcete priamo načítať originál alebo použiť iba miniatúru.",
|
||||
"setting_image_viewer_preview_subtitle": "Povolením umožníte načítať obrázok so stredným rozlíšením. Zakážte, ak chcete priamo načítať originál alebo použiť iba miniatúru.",
|
||||
"setting_image_viewer_preview_title": "Načítať náhľad obrázka",
|
||||
"setting_notifications_notify_failures_grace_period": "Oznámenie o zlyhaní zálohovania na pozadí: {}",
|
||||
"setting_notifications_notify_hours": "{} hodín",
|
||||
|
||||
233
mobile/assets/i18n/sv-SE.json
Normal file
@@ -0,0 +1,233 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
||||
"album_info_card_backup_album_excluded": "EXKLUDERAD",
|
||||
"album_info_card_backup_album_included": "INKLUDERAD",
|
||||
"album_thumbnail_card_item": "1 objekt",
|
||||
"album_thumbnail_card_items": "{} objekt",
|
||||
"album_thumbnail_card_shared": ". Delad",
|
||||
"album_viewer_appbar_share_delete": "Radera album",
|
||||
"album_viewer_appbar_share_err_delete": "Kunde inte radera album",
|
||||
"album_viewer_appbar_share_err_leave": "Kunde inte lämna album",
|
||||
"album_viewer_appbar_share_err_remove": "Kunde inte ta bort objekt från album",
|
||||
"album_viewer_appbar_share_err_title": "Kunde inte ändra albumtitel",
|
||||
"album_viewer_appbar_share_leave": "Lämna album",
|
||||
"album_viewer_appbar_share_remove": "Ta bort från album",
|
||||
"album_viewer_page_share_add_users": "Lägg till användare",
|
||||
"asset_list_layout_settings_dynamic_layout_title": "Dynamisk layout",
|
||||
"asset_list_layout_settings_group_by": "Gruppera bilder efter",
|
||||
"asset_list_layout_settings_group_by_month": "Månad",
|
||||
"asset_list_layout_settings_group_by_month_day": "Månad + dag",
|
||||
"asset_list_settings_subtitle": "Layoutinställningar för bildrutnät",
|
||||
"asset_list_settings_title": "Bildrutnät",
|
||||
"backup_album_selection_page_albums_device": "Album på enhet ({})",
|
||||
"backup_album_selection_page_albums_tap": "Tryck en gång för att inkludera, tryck två gånger för att exkludera",
|
||||
"backup_album_selection_page_assets_scatter": "Objekt kan vara utspridda över flera album. Därför kan album inkluderas eller exkluderas under säkerhetskopieringsprocessen",
|
||||
"backup_album_selection_page_select_albums": "Välj album",
|
||||
"backup_album_selection_page_selection_info": "Info om valda objekt",
|
||||
"backup_album_selection_page_total_assets": "Antal unika objekt",
|
||||
"backup_all": "Allt",
|
||||
"backup_background_service_backup_failed_message": "Säkerhetskopiering av foton och videor misslyckades. Försöker igen...",
|
||||
"backup_background_service_connection_failed_message": "Anslutningen till servern förlorades. Försöker igen...",
|
||||
"backup_background_service_current_upload_notification": "Laddar upp {}",
|
||||
"backup_background_service_default_notification": "Söker efter nya objekt...",
|
||||
"backup_background_service_error_title": "Fel i säkerhetskopiering",
|
||||
"backup_background_service_in_progress_notification": "Säkerhetskopierar dina foton och videor...",
|
||||
"backup_background_service_upload_failure_notification": "Kunde inte ladda upp {}",
|
||||
"backup_controller_page_albums": "Säkerhetskopiera album",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "Visa mig hur",
|
||||
"backup_controller_page_background_battery_info_message": "För optimal säkerhetskopiering i bakgrunden bör du stänga av batterioptimering som begränsar bakgrundsaktivitet för Immich.\n\nEftersom detta är enhetsspecifikt så bör du söka instruktioner från din enhetstillverkare.",
|
||||
"backup_controller_page_background_battery_info_ok": "OK",
|
||||
"backup_controller_page_background_battery_info_title": "Batterioptimering",
|
||||
"backup_controller_page_background_charging": "Endast vid laddning",
|
||||
"backup_controller_page_background_configure_error": "Kunde inte konfigurera bakgrundstjänsten",
|
||||
"backup_controller_page_background_delay": "Skjut upp säkerhetskopiering av nya foton och videor: {}",
|
||||
"backup_controller_page_background_description": "Aktivera säkerhetskopiering i bakgrunden för att automatiskt säkerhetskopiera nya foton och videor utan att öppna appen",
|
||||
"backup_controller_page_background_is_off": "Automatisk säkerhetskopiering i bakgrunden är avstängd",
|
||||
"backup_controller_page_background_is_on": "Automatisk säkerhetskopiering i bakgrunden är aktiverad",
|
||||
"backup_controller_page_background_turn_off": "Stäng av säkerhetskopiering i bakgrunden",
|
||||
"backup_controller_page_background_turn_on": "Aktivera säkerhetskopiering i bakgrunden",
|
||||
"backup_controller_page_background_wifi": "Endast med WiFi",
|
||||
"backup_controller_page_backup": "Säkerhetskopiera",
|
||||
"backup_controller_page_backup_selected": "Valt: ",
|
||||
"backup_controller_page_backup_sub": "Säkerhetskopierade foton och videor",
|
||||
"backup_controller_page_cancel": "Avbryt",
|
||||
"backup_controller_page_created": "Skapad den: {}",
|
||||
"backup_controller_page_desc_backup": "Aktivera förgrunds-säkerhetskopiering för att automatiskt ladda upp nya foton och videor när du öppnar appen.",
|
||||
"backup_controller_page_excluded": "Exkluderat: ",
|
||||
"backup_controller_page_failed": "Misslyckades ({})",
|
||||
"backup_controller_page_filename": "Filnamn: {} [{}]",
|
||||
"backup_controller_page_id": "ID: {}",
|
||||
"backup_controller_page_info": "Säkerhetskopieringsinformation",
|
||||
"backup_controller_page_none_selected": "Ingenting valt",
|
||||
"backup_controller_page_remainder": "Resterande",
|
||||
"backup_controller_page_remainder_sub": "Återstående foton och album att säkerhetskopiera från valda",
|
||||
"backup_controller_page_select": "Välj",
|
||||
"backup_controller_page_server_storage": "Serverlagring",
|
||||
"backup_controller_page_start_backup": "Starta säkerhetskopiering",
|
||||
"backup_controller_page_status_off": "Automatisk säkerhetskopiering är avstängd",
|
||||
"backup_controller_page_status_on": "Automatisk säkerhetskopiering är aktiverad",
|
||||
"backup_controller_page_storage_format": "{} av {} brukat",
|
||||
"backup_controller_page_to_backup": "Album att säkerhetskopiera",
|
||||
"backup_controller_page_total": "Sammanlagt",
|
||||
"backup_controller_page_total_sub": "Alla unika foton och videor från valda album",
|
||||
"backup_controller_page_turn_off": "Stäng av automatisk säkerhetskopiering",
|
||||
"backup_controller_page_turn_on": "Aktivera automatisk säkerhetskopiering",
|
||||
"backup_controller_page_uploading_file_info": "Laddar upp filinformation",
|
||||
"backup_err_only_album": "Kan inte ta bort det enda albumet",
|
||||
"backup_info_card_assets": "objekt",
|
||||
"cache_settings_album_thumbnails": "Miniatyrbilder för bibliotek ({} bilder och videor)",
|
||||
"cache_settings_clear_cache_button": "Rensa cacheminnet",
|
||||
"cache_settings_clear_cache_button_title": "Rensar appens cacheminne. Detta kommer att avsevärt påverka appens prestanda tills cachen har byggts om.",
|
||||
"cache_settings_image_cache_size": "Cacheminnets storlek ({} bilder och videor)",
|
||||
"cache_settings_statistics_album": "Miniatyrbilder för bibliotek",
|
||||
"cache_settings_statistics_assets": "{} bilder och videor ({})",
|
||||
"cache_settings_statistics_full": "Hela bilder",
|
||||
"cache_settings_statistics_shared": "Miniatyrbilder till delat album",
|
||||
"cache_settings_statistics_thumbnail": "Miniatyrbilder",
|
||||
"cache_settings_statistics_title": "Cacheförbrukning",
|
||||
"cache_settings_subtitle": "Hantera cachebeteendet för Immich-appen.",
|
||||
"cache_settings_thumbnail_size": "Storlek på cacheminnet ({} bilder och videor)",
|
||||
"cache_settings_title": "Cache Inställningar",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
"common_add_to_album": "Add to album",
|
||||
"common_change_password": "Change Password",
|
||||
"common_create_new_album": "Create new album",
|
||||
"common_shared": "Shared",
|
||||
"control_bottom_app_bar_add_to_album": "Lägg till i album",
|
||||
"control_bottom_app_bar_album_info": "{} objekt",
|
||||
"control_bottom_app_bar_album_info_shared": "{} objekt • Delat",
|
||||
"control_bottom_app_bar_create_new_album": "Skapa nytt album",
|
||||
"control_bottom_app_bar_delete": "Radera",
|
||||
"control_bottom_app_bar_favorite": "Favorite",
|
||||
"control_bottom_app_bar_share": "Dela",
|
||||
"create_album_page_untitled": "Namnlös",
|
||||
"create_shared_album_page_create": "Skapa",
|
||||
"create_shared_album_page_share": "Dela",
|
||||
"create_shared_album_page_share_add_assets": "LÄGG TILL OBJEKT",
|
||||
"create_shared_album_page_share_select_photos": "Välj bilder",
|
||||
"daily_title_text_date": "E, dd MMM",
|
||||
"daily_title_text_date_year": "E, dd MMM, yyyy",
|
||||
"date_format": "E d. LLL y • hh:mm",
|
||||
"delete_dialog_alert": "Dessa objekt kommer att raderas permanent från Immich och din enhet",
|
||||
"delete_dialog_cancel": "Avbryt",
|
||||
"delete_dialog_ok": "Radera",
|
||||
"delete_dialog_title": "Radera permanent",
|
||||
"exif_bottom_sheet_description": "Lägg till beskrivning...",
|
||||
"exif_bottom_sheet_details": "DETALJER",
|
||||
"exif_bottom_sheet_location": "PLATS",
|
||||
"experimental_settings_new_asset_list_subtitle": "Under uppbyggnad",
|
||||
"experimental_settings_new_asset_list_title": "Aktivera experimentellt fotorutnät",
|
||||
"experimental_settings_subtitle": "Använd på egen risk!",
|
||||
"experimental_settings_title": "Experimentellt",
|
||||
"favorites_page_title": "Favoriter",
|
||||
"home_page_add_to_album_conflicts": "Lade till {added} foton och videor i albumet {album}. {failed} foton och videor finns redan i albumet.",
|
||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
||||
"home_page_add_to_album_success": "Lade till {added} foton och videor i albumet {album}.",
|
||||
"home_page_building_timeline": "Bygger tidslinjen",
|
||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
||||
"home_page_first_time_notice": "Om det här är första gången du använder appen, välj ett eller flera backup-album så att tidslinjen kan fyllas med foton och videor från albumen.",
|
||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
||||
"library_page_albums": "Album",
|
||||
"library_page_favorites": "Favoriter",
|
||||
"library_page_new_album": "Nytt album",
|
||||
"library_page_sharing": "Delas",
|
||||
"library_page_sort_created": "Senast skapad",
|
||||
"library_page_sort_title": "Albumtitel",
|
||||
"login_form_button_text": "Logga in",
|
||||
"login_form_email_hint": "din.email@email.com",
|
||||
"login_form_endpoint_hint": "http://din-server-ip:port/api",
|
||||
"login_form_endpoint_url": "Server Endpoint URL",
|
||||
"login_form_err_http": "Var god ange http:// eller https://",
|
||||
"login_form_err_invalid_email": "Ogiltig email",
|
||||
"login_form_err_invalid_url": "Ogiltig webbadress",
|
||||
"login_form_err_leading_whitespace": "Mellanrum före",
|
||||
"login_form_err_trailing_whitespace": "Mellanrum efter",
|
||||
"login_form_failed_get_oauth_server_config": "Kunde inte logga in med OAuth. Kontrollera serverns webbadress",
|
||||
"login_form_failed_get_oauth_server_disable": "OAuth är inte tillgänglig på den här servern",
|
||||
"login_form_failed_login": "Kunde inte logga in. Kontrollera serverns webbadress, email och lösenord.",
|
||||
"login_form_label_email": "Email",
|
||||
"login_form_label_password": "Lösenord",
|
||||
"login_form_password_hint": "lösenord",
|
||||
"login_form_save_login": "Håll mig inloggad",
|
||||
"monthly_title_text_date_format": "MMMM y",
|
||||
"notification_permission_dialog_cancel": "Cancel",
|
||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
||||
"notification_permission_dialog_settings": "Settings",
|
||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
||||
"notification_permission_list_tile_title": "Notification Permission",
|
||||
"profile_drawer_app_logs": "Loggar",
|
||||
"profile_drawer_client_server_up_to_date": "Klient och server är uppdaterade",
|
||||
"profile_drawer_settings": "Inställningar",
|
||||
"profile_drawer_sign_out": "Logga ut",
|
||||
"search_bar_hint": "Sök bland dina foton",
|
||||
"search_page_no_objects": "Inga objekt är tillgängliga",
|
||||
"search_page_no_places": "Ingen platsinformation finns tillgänglig",
|
||||
"search_page_places": "Platser",
|
||||
"search_page_things": "Saker",
|
||||
"search_result_page_new_search_hint": "Ny sökning",
|
||||
"select_additional_user_for_sharing_page_suggestions": "Förslag",
|
||||
"select_user_for_sharing_page_err_album": "Kunde inte skapa nytt album",
|
||||
"select_user_for_sharing_page_share_suggestions": "Förslag",
|
||||
"server_info_box_app_version": "App Version",
|
||||
"server_info_box_server_version": "Server Version",
|
||||
"setting_image_viewer_help": "Detaljerad vy laddar miniatyrer först. Efter detta laddas den medelstora förhandsgranskningen av bilden (om detta är aktiverat), och visar slutligen originalet (om detta är aktiverat).",
|
||||
"setting_image_viewer_original_subtitle": "Aktivera för att ladda originalbilden i full storlek (stor!). Inaktivera för att minska dataanvändningen (både i nätverket och för enhetscache).",
|
||||
"setting_image_viewer_original_title": "Ladda originalbilden",
|
||||
"setting_image_viewer_preview_subtitle": "Aktivera för att ladda en mellanstor bild. Stäng av för att antingen ladda originalet direkt eller bara använda miniatyrbilden.",
|
||||
"setting_image_viewer_preview_title": "Ladda förhandsgranskning av bild",
|
||||
"setting_notifications_notify_failures_grace_period": "Rapportera säkerhetskopieringsfel i bakgrunden: {}",
|
||||
"setting_notifications_notify_hours": "{} timmar",
|
||||
"setting_notifications_notify_immediately": "omedelbart",
|
||||
"setting_notifications_notify_minutes": "{} minuter",
|
||||
"setting_notifications_notify_never": "aldrig",
|
||||
"setting_notifications_notify_seconds": "{} sekunder",
|
||||
"setting_notifications_single_progress_subtitle": "Detaljerad uppladdningsstatus per bild och video",
|
||||
"setting_notifications_single_progress_title": "Visa detaljerat uppladdningsförlopp",
|
||||
"setting_notifications_subtitle": "Anpassa dina notifikations-inställningar",
|
||||
"setting_notifications_title": "Notifikationer",
|
||||
"setting_notifications_total_progress_subtitle": "Övergripande uppladdningsförlopp (klar/totala tillgångar)",
|
||||
"setting_notifications_total_progress_title": "Visa totalt uppladdningsförlopp",
|
||||
"setting_pages_app_bar_settings": "Inställningar",
|
||||
"settings_require_restart": "Starta om Immich för att tillämpa den här inställningen",
|
||||
"share_add": "Lägg till",
|
||||
"share_add_photos": "Lägg till foton",
|
||||
"share_add_title": "Lägg till en titel",
|
||||
"share_create_album": "Skapa album",
|
||||
"share_dialog_preparing": "Förbereder...",
|
||||
"share_invite": "Bjuder in till album",
|
||||
"sharing_page_album": "Delade album",
|
||||
"sharing_page_description": "Skapa delade album för att dela foton och video med personer i ditt nätverk.",
|
||||
"sharing_page_empty_list": "TOM LISTA",
|
||||
"sharing_silver_appbar_create_shared_album": "Skapa delat album",
|
||||
"sharing_silver_appbar_share_partner": "Dela med partner",
|
||||
"tab_controller_nav_library": "Bibliotek",
|
||||
"tab_controller_nav_photos": "Bilder",
|
||||
"tab_controller_nav_search": "Sök",
|
||||
"tab_controller_nav_sharing": "Delning",
|
||||
"theme_setting_asset_list_storage_indicator_title": "Visa lagringsindikator på filer",
|
||||
"theme_setting_asset_list_tiles_per_row_title": "Antal bilder och videor per rad ({})",
|
||||
"theme_setting_dark_mode_switch": "Mörkt läge",
|
||||
"theme_setting_image_viewer_quality_subtitle": "Justera kvaliteten i bildvisaren",
|
||||
"theme_setting_image_viewer_quality_title": "Bildvisarens kvalitet",
|
||||
"theme_setting_system_theme_switch": "Automatisk (Följ systeminställningar)",
|
||||
"theme_setting_theme_subtitle": "Välj inställning för appens tema",
|
||||
"theme_setting_theme_title": "Tema",
|
||||
"theme_setting_three_stage_loading_subtitle": "Trestegsladdning kan öka prestandan, men kan också leda till signifikant högre nätverksbelastning",
|
||||
"theme_setting_three_stage_loading_title": "Aktivera trestegsladdning",
|
||||
"version_announcement_overlay_ack": "Bekräfta",
|
||||
"version_announcement_overlay_release_notes": "versionsinformation",
|
||||
"version_announcement_overlay_text_1": "Hej vännen, det finns en ny version av",
|
||||
"version_announcement_overlay_text_2": ". Ta gärna din tid att besöka ",
|
||||
"version_announcement_overlay_text_3": " för att se till att din docker-compose och .env-fil är uppdaterad för att undvika felkonfiguration, speciellt om du använder WatchTower eller liknande mekanism som automatiskt uppdaterar din container",
|
||||
"version_announcement_overlay_title": "Ny serverversion är tillgänglig \uD83C\uDF89"
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"add_to_album_bottom_sheet_added": "Added to {album}",
|
||||
"add_to_album_bottom_sheet_already_exists": "Already in {album}",
|
||||
"album_info_card_backup_album_excluded": "排除",
|
||||
"album_info_card_backup_album_included": "已选",
|
||||
"album_thumbnail_card_item": "1张",
|
||||
@@ -33,6 +35,9 @@
|
||||
"backup_background_service_in_progress_notification": "正在备份…",
|
||||
"backup_background_service_upload_failure_notification": "上传失败 {}",
|
||||
"backup_controller_page_albums": "备份相册",
|
||||
"backup_controller_page_background_app_refresh_disabled_content": "Enable background app refresh in Settings > General > Background App Refresh in order to use background backup.",
|
||||
"backup_controller_page_background_app_refresh_disabled_title": "Background app refresh disabled",
|
||||
"backup_controller_page_background_app_refresh_enable_button_text": "Go to settings",
|
||||
"backup_controller_page_background_battery_info_link": "怎么做",
|
||||
"backup_controller_page_background_battery_info_message": "为了获得最佳的后台备份体验,请禁用任何限制 Immich 后台活动的电池优化。\n\n由于这是设备相关的,因此请查找设备制造商所需的信息。",
|
||||
"backup_controller_page_background_battery_info_ok": "我知道了",
|
||||
@@ -87,11 +92,21 @@
|
||||
"cache_settings_subtitle": "控制 Immich的缓存表现",
|
||||
"cache_settings_thumbnail_size": "缩略图缓存大小({}张)",
|
||||
"cache_settings_title": "缓存设置",
|
||||
"change_password_form_confirm_password": "Confirm Password",
|
||||
"change_password_form_description": "Hi {firstName} {lastName},\n\nThis is either the first time you are signing into the system or a request has been made to change your password. Please enter the new password below.",
|
||||
"change_password_form_new_password": "New Password",
|
||||
"change_password_form_password_mismatch": "Passwords do not match",
|
||||
"change_password_form_reenter_new_password": "Re-enter New Password",
|
||||
"common_add_to_album": "Add to album",
|
||||
"common_change_password": "Change Password",
|
||||
"common_create_new_album": "Create new album",
|
||||
"common_shared": "Shared",
|
||||
"control_bottom_app_bar_add_to_album": "添加到相册",
|
||||
"control_bottom_app_bar_album_info": "{}张",
|
||||
"control_bottom_app_bar_album_info_shared": "{} 张已分享",
|
||||
"control_bottom_app_bar_create_new_album": "新建相册",
|
||||
"control_bottom_app_bar_delete": "删除",
|
||||
"control_bottom_app_bar_favorite": "Favorite",
|
||||
"control_bottom_app_bar_share": "分享",
|
||||
"create_album_page_untitled": "未命名",
|
||||
"create_shared_album_page_create": "新建",
|
||||
@@ -114,9 +129,13 @@
|
||||
"experimental_settings_title": "实验功能",
|
||||
"favorites_page_title": "Favorites",
|
||||
"home_page_add_to_album_conflicts": "添加{added}张到相册{album}。{failed} 项已经处于该相册中。",
|
||||
"home_page_add_to_album_err_local": "Can not add local assets to albums yet, skipping",
|
||||
"home_page_add_to_album_success": "添加了{added}张到相册{album}。",
|
||||
"home_page_building_timeline": "Building the timeline",
|
||||
"home_page_favorite_err_local": "Can not favorite local assets yet, skipping",
|
||||
"home_page_first_time_notice": "If this is your first time using the app, please make sure to choose a backup album(s) so that the timeline can populate photos and videos in the album(s).",
|
||||
"image_viewer_page_state_provider_download_error": "Download Error",
|
||||
"image_viewer_page_state_provider_download_success": "Download Success",
|
||||
"library_page_albums": "相册",
|
||||
"library_page_favorites": "Favorites",
|
||||
"library_page_new_album": "新建相册",
|
||||
@@ -140,6 +159,12 @@
|
||||
"login_form_password_hint": "密码",
|
||||
"login_form_save_login": "保持登录",
|
||||
"monthly_title_text_date_format": "MMMM y",
|
||||
"notification_permission_dialog_cancel": "Cancel",
|
||||
"notification_permission_dialog_content": "To enable notifications, go to Settings and select allow.",
|
||||
"notification_permission_dialog_settings": "Settings",
|
||||
"notification_permission_list_tile_content": "Grant permission to enable notifications.",
|
||||
"notification_permission_list_tile_enable_button": "Enable Notifications",
|
||||
"notification_permission_list_tile_title": "Notification Permission",
|
||||
"profile_drawer_app_logs": "日志",
|
||||
"profile_drawer_client_server_up_to_date": "客户端和服务端都是最新的",
|
||||
"profile_drawer_settings": "设置",
|
||||
@@ -153,6 +178,8 @@
|
||||
"select_additional_user_for_sharing_page_suggestions": "建议",
|
||||
"select_user_for_sharing_page_err_album": "创建相册失败",
|
||||
"select_user_for_sharing_page_share_suggestions": "建议",
|
||||
"server_info_box_app_version": "App Version",
|
||||
"server_info_box_server_version": "Server Version",
|
||||
"setting_image_viewer_help": "查看大图时会首先加载缩略图,然后加载中等质量的图片(如果启用),最后加载原始质量的图片(如果启用)。",
|
||||
"setting_image_viewer_original_subtitle": "开启将会加载原图。关闭将会减少内存和网络占用。",
|
||||
"setting_image_viewer_original_title": "加载原图",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
@@ -43,7 +45,6 @@ class ImmichTestHelper {
|
||||
// Load main Widget
|
||||
await tester.pumpWidget(app.getMainWidget(db));
|
||||
// Post run tasks
|
||||
await tester.pumpAndSettle();
|
||||
await EasyLocalization.ensureInitialized();
|
||||
}
|
||||
}
|
||||
@@ -62,3 +63,17 @@ void immichWidgetTest(
|
||||
semanticsEnabled: false,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> pumpUntilFound(
|
||||
WidgetTester tester,
|
||||
Finder finder, {
|
||||
Duration timeout = const Duration(seconds: 120),
|
||||
}) async {
|
||||
bool found = false;
|
||||
final timer = Timer(timeout, () => throw TimeoutException("Pump until has timed out"));
|
||||
while (found != true) {
|
||||
await tester.pump();
|
||||
found = tester.any(finder);
|
||||
}
|
||||
timer.cancel();
|
||||
}
|
||||
|
||||
@@ -2,33 +2,20 @@ import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import 'general_helper.dart';
|
||||
|
||||
class ImmichTestLoginHelper {
|
||||
final WidgetTester tester;
|
||||
|
||||
ImmichTestLoginHelper(this.tester);
|
||||
|
||||
Future<void> waitForLoginScreen({int timeoutSeconds = 20}) async {
|
||||
for (var i = 0; i < timeoutSeconds; i++) {
|
||||
// Search for "IMMICH" test in the app bar
|
||||
final result = find.text("IMMICH");
|
||||
if (tester.any(result)) {
|
||||
// Wait 5s until everything settled
|
||||
await tester.pump(const Duration(seconds: 5));
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait 1s before trying again
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
|
||||
fail("Timeout while waiting for login screen");
|
||||
Future<void> waitForLoginScreen() async {
|
||||
await pumpUntilFound(tester, find.text("Login"));
|
||||
}
|
||||
|
||||
Future<bool> acknowledgeNewServerVersion() async {
|
||||
await pumpUntilFound(tester, find.text("Acknowledge"));
|
||||
final result = find.text("Acknowledge");
|
||||
if (!tester.any(result)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await tester.tap(result);
|
||||
await tester.pump();
|
||||
@@ -43,17 +30,17 @@ class ImmichTestLoginHelper {
|
||||
}) async {
|
||||
final loginForms = find.byType(TextFormField);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
await tester.enterText(loginForms.at(0), email);
|
||||
await tester.pump();
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
await tester.enterText(loginForms.at(1), password);
|
||||
await tester.pump();
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
await tester.enterText(loginForms.at(2), server);
|
||||
await tester.pump();
|
||||
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pump();
|
||||
}
|
||||
|
||||
Future<void> enterCredentialsOf(LoginCredentials credentials) async {
|
||||
@@ -65,32 +52,17 @@ class ImmichTestLoginHelper {
|
||||
}
|
||||
|
||||
Future<void> pressLoginButton() async {
|
||||
await pumpUntilFound(tester, find.textContaining("login_form_button_text".tr()));
|
||||
final button = find.textContaining("login_form_button_text".tr());
|
||||
await tester.tap(button);
|
||||
}
|
||||
|
||||
Future<void> assertLoginSuccess({int timeoutSeconds = 15}) async {
|
||||
for (var i = 0; i < timeoutSeconds * 2; i++) {
|
||||
if (tester.any(find.text("home_page_building_timeline".tr()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
}
|
||||
|
||||
fail("Login failed.");
|
||||
await pumpUntilFound(tester, find.text("home_page_building_timeline".tr()));
|
||||
}
|
||||
|
||||
Future<void> assertLoginFailed({int timeoutSeconds = 15}) async {
|
||||
for (var i = 0; i < timeoutSeconds * 2; i++) {
|
||||
if (tester.any(find.text("login_form_failed_login".tr()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 500));
|
||||
}
|
||||
|
||||
fail("Timeout.");
|
||||
await pumpUntilFound(tester, find.text("login_form_failed_login".tr()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,5 +37,66 @@ end
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
flutter_additional_ios_build_settings(target)
|
||||
|
||||
# Permission macros for the permission_handler (https://pub.dev/packages/permission_handler)
|
||||
# Start of the permission_handler configuration
|
||||
# Remove the # character in front of the permission you do want to use.
|
||||
target.build_configurations.each do |config|
|
||||
|
||||
# You can enable the permissions needed here. For example to enable camera
|
||||
# permission, just remove the `#` character in front so it looks like this:
|
||||
#
|
||||
# ## dart: PermissionGroup.camera
|
||||
# 'PERMISSION_CAMERA=1'
|
||||
#
|
||||
# Preprocessor definitions can be found in: https://github.com/Baseflow/flutter-permission-handler/blob/master/permission_handler_apple/ios/Classes/PermissionHandlerEnums.h
|
||||
config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [
|
||||
'$(inherited)',
|
||||
|
||||
## dart: PermissionGroup.calendar
|
||||
# 'PERMISSION_EVENTS=1',
|
||||
|
||||
## dart: PermissionGroup.reminders
|
||||
# 'PERMISSION_REMINDERS=1',
|
||||
|
||||
## dart: PermissionGroup.contacts
|
||||
# 'PERMISSION_CONTACTS=1',
|
||||
|
||||
## dart: PermissionGroup.camera
|
||||
# 'PERMISSION_CAMERA=1',
|
||||
|
||||
## dart: PermissionGroup.microphone
|
||||
# 'PERMISSION_MICROPHONE=1',
|
||||
|
||||
## dart: PermissionGroup.speech
|
||||
# 'PERMISSION_SPEECH_RECOGNIZER=1',
|
||||
|
||||
## dart: PermissionGroup.photos
|
||||
'PERMISSION_PHOTOS=1',
|
||||
|
||||
## dart: [PermissionGroup.location, PermissionGroup.locationAlways, PermissionGroup.locationWhenInUse]
|
||||
# 'PERMISSION_LOCATION=1',
|
||||
|
||||
## dart: PermissionGroup.notification
|
||||
'PERMISSION_NOTIFICATIONS=1',
|
||||
|
||||
## dart: PermissionGroup.mediaLibrary
|
||||
# 'PERMISSION_MEDIA_LIBRARY=1',
|
||||
|
||||
## dart: PermissionGroup.sensors
|
||||
# 'PERMISSION_SENSORS=1',
|
||||
|
||||
## dart: PermissionGroup.bluetooth
|
||||
# 'PERMISSION_BLUETOOTH=1',
|
||||
|
||||
## dart: PermissionGroup.appTrackingTransparency
|
||||
# 'PERMISSION_APP_TRACKING_TRANSPARENCY=1',
|
||||
|
||||
## dart: PermissionGroup.criticalAlerts
|
||||
# 'PERMISSION_CRITICAL_ALERTS=1'
|
||||
]
|
||||
|
||||
end
|
||||
# End of the permission_handler configuration
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
PODS:
|
||||
- device_info_plus (0.0.1):
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- flutter_native_splash (0.0.1):
|
||||
- Flutter
|
||||
@@ -24,6 +26,10 @@ PODS:
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- path_provider_ios (0.0.1):
|
||||
- Flutter
|
||||
- permission_handler_apple (9.0.4):
|
||||
- Flutter
|
||||
- photo_manager (2.0.0):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
@@ -45,6 +51,7 @@ PODS:
|
||||
- Flutter
|
||||
|
||||
DEPENDENCIES:
|
||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||
- flutter_udid (from `.symlinks/plugins/flutter_udid/ios`)
|
||||
@@ -55,6 +62,8 @@ DEPENDENCIES:
|
||||
- isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`)
|
||||
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
|
||||
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
|
||||
- photo_manager (from `.symlinks/plugins/photo_manager/ios`)
|
||||
- share_plus (from `.symlinks/plugins/share_plus/ios`)
|
||||
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`)
|
||||
@@ -70,6 +79,8 @@ SPEC REPOS:
|
||||
- Toast
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
device_info_plus:
|
||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
flutter_native_splash:
|
||||
@@ -90,6 +101,10 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/ios"
|
||||
path_provider_ios:
|
||||
:path: ".symlinks/plugins/path_provider_ios/ios"
|
||||
permission_handler_apple:
|
||||
:path: ".symlinks/plugins/permission_handler_apple/ios"
|
||||
photo_manager:
|
||||
:path: ".symlinks/plugins/photo_manager/ios"
|
||||
share_plus:
|
||||
@@ -106,27 +121,30 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/wakelock/ios"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed
|
||||
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
|
||||
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
|
||||
flutter_udid: 0848809dbed4c055175747ae6a45a8b4f6771e1c
|
||||
flutter_web_auth: c25208760459cec375a3c39f6a8759165ca0fa4d
|
||||
fluttertoast: eb263d302cc92e04176c053d2385237e9f43fad0
|
||||
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
|
||||
image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb
|
||||
image_picker_ios: 58b9c4269cb176f89acea5e5d043c9358f2d25f8
|
||||
integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5
|
||||
isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073
|
||||
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e
|
||||
path_provider_foundation: 37748e03f12783f9de2cb2c4eadfaa25fe6d4852
|
||||
path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9
|
||||
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
|
||||
permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce
|
||||
photo_manager: 4f6810b7dfc4feb03b461ac1a70dacf91fba7604
|
||||
SAMKeychain: 483e1c9f32984d50ca961e26818a534283b4cd5c
|
||||
share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68
|
||||
shared_preferences_foundation: 297b3ebca31b34ec92be11acd7fb0ba932c822ca
|
||||
shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472
|
||||
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904
|
||||
Toast: 91b396c56ee72a5790816f40d3a94dd357abc196
|
||||
url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2
|
||||
video_player_avfoundation: e489aac24ef5cf7af82702979ed16f2a5ef84cff
|
||||
url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4
|
||||
video_player_avfoundation: 6d971a232d72e6ee25368378d48a079dea01f1cf
|
||||
wakelock: d0fc7c864128eac40eba1617cb5264d9c940b46f
|
||||
|
||||
PODFILE CHECKSUM: c798208781ca5116c4a3d5927d689946791f0189
|
||||
PODFILE CHECKSUM: 0606648e8a9ecd5a59eafa5ab3187b45a9004a28
|
||||
|
||||
COCOAPODS: 1.11.3
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
65F32F31299BD2F800CE9261 /* BackgroundServicePlugin.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F32F30299BD2F800CE9261 /* BackgroundServicePlugin.swift */; };
|
||||
65F32F33299D349D00CE9261 /* BackgroundSyncWorker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65F32F32299D349D00CE9261 /* BackgroundSyncWorker.swift */; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
@@ -34,6 +36,8 @@
|
||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||
2E3441B73560D0F6FD25E04F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
65F32F30299BD2F800CE9261 /* BackgroundServicePlugin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundServicePlugin.swift; sourceTree = "<group>"; };
|
||||
65F32F32299D349D00CE9261 /* BackgroundSyncWorker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BackgroundSyncWorker.swift; sourceTree = "<group>"; };
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||
@@ -80,6 +84,15 @@
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
65DD438629917FAD0047FFA8 /* BackgroundSync */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
65F32F32299D349D00CE9261 /* BackgroundSyncWorker.swift */,
|
||||
65F32F30299BD2F800CE9261 /* BackgroundServicePlugin.swift */,
|
||||
);
|
||||
path = BackgroundSync;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -113,6 +126,7 @@
|
||||
97C146F01CF9000F007C117D /* Runner */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
65DD438629917FAD0047FFA8 /* BackgroundSync */,
|
||||
FAC7416727DB9F5500C668D8 /* RunnerProfile.entitlements */,
|
||||
97C146FA1CF9000F007C117D /* Main.storyboard */,
|
||||
97C146FD1CF9000F007C117D /* Assets.xcassets */,
|
||||
@@ -275,8 +289,10 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
65F32F31299BD2F800CE9261 /* BackgroundServicePlugin.swift in Sources */,
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
|
||||
65F32F33299D349D00CE9261 /* BackgroundSyncWorker.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -362,8 +378,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/RunnerProfile.entitlements;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
@@ -372,6 +387,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -498,8 +514,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
@@ -508,6 +523,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -526,8 +542,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
|
||||
CURRENT_PROJECT_VERSION = 88;
|
||||
DEVELOPMENT_TEAM = 2F67MQ8R79;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
@@ -536,6 +551,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = "$(FLUTTER_BUILD_NAME)";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = app.alextran.immich;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
||||
@@ -43,11 +43,13 @@
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
disableMainThreadChecker = "YES"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
enableGPUValidationMode = "1"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
|
||||
@@ -1,13 +1,43 @@
|
||||
import UIKit
|
||||
import shared_preferences_foundation
|
||||
import Flutter
|
||||
import BackgroundTasks
|
||||
import path_provider_ios
|
||||
import photo_manager
|
||||
import permission_handler_apple
|
||||
|
||||
@UIApplicationMain
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
BackgroundServicePlugin.registerBackgroundProcessing()
|
||||
|
||||
BackgroundServicePlugin.register(with: self.registrar(forPlugin: "BackgroundServicePlugin")!)
|
||||
|
||||
BackgroundServicePlugin.setPluginRegistrantCallback { registry in
|
||||
if !registry.hasPlugin("org.cocoapods.path-provider-ios") {
|
||||
FLTPathProviderPlugin.register(with: registry.registrar(forPlugin: "org.cocoapods.path-provider-ios")!)
|
||||
}
|
||||
|
||||
if !registry.hasPlugin("org.cocoapods.photo-manager") {
|
||||
PhotoManagerPlugin.register(with: registry.registrar(forPlugin: "org.cocoapods.photo-manager")!)
|
||||
}
|
||||
|
||||
if !registry.hasPlugin("org.cocoapods.shared-preferences-foundation") {
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "org.cocoapods.shared-preferences-foundation")!)
|
||||
}
|
||||
|
||||
if !registry.hasPlugin("org.cocoapods.permission-handler-apple") {
|
||||
PermissionHandlerPlugin.register(with: registry.registrar(forPlugin: "org.cocoapods.permission-handler-apple")!)
|
||||
}
|
||||
}
|
||||
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
6
mobile/ios/Runner/Assets.xcassets/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
342
mobile/ios/Runner/BackgroundSync/BackgroundServicePlugin.swift
Normal file
@@ -0,0 +1,342 @@
|
||||
//
|
||||
// BackgroundServicePlugin.swift
|
||||
// Runner
|
||||
//
|
||||
// Created by Marty Fuhry on 2/14/23.
|
||||
//
|
||||
|
||||
import Flutter
|
||||
import BackgroundTasks
|
||||
import path_provider_foundation
|
||||
|
||||
class BackgroundServicePlugin: NSObject, FlutterPlugin {
|
||||
|
||||
public static var flutterPluginRegistrantCallback: FlutterPluginRegistrantCallback?
|
||||
|
||||
public static func setPluginRegistrantCallback(_ callback: FlutterPluginRegistrantCallback) {
|
||||
flutterPluginRegistrantCallback = callback
|
||||
}
|
||||
|
||||
// Pause the application in XCode, then enter
|
||||
// e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"app.alextran.immich.backgroundFetch"]
|
||||
// or
|
||||
// e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"app.alextran.immich.backgroundProcessing"]
|
||||
// Then resume the application see the background code run
|
||||
// Tested on a physical device, not a simulator
|
||||
// This will submit either the Fetch or Processing command to the BGTaskScheduler for immediate processing.
|
||||
// In my tests, I can only get app.alextran.immich.backgroundProcessing simulated by running the above command
|
||||
|
||||
// This is the task ID in Info.plist to register as our background task ID
|
||||
public static let backgroundFetchTaskID = "app.alextran.immich.backgroundFetch"
|
||||
public static let backgroundProcessingTaskID = "app.alextran.immich.backgroundProcessing"
|
||||
|
||||
// Establish communication with the main isolate and set up the channel call
|
||||
// to this BackgroundServicePlugion()
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
let channel = FlutterMethodChannel(
|
||||
name: "immich/foregroundChannel",
|
||||
binaryMessenger: registrar.messenger()
|
||||
)
|
||||
|
||||
let instance = BackgroundServicePlugin()
|
||||
registrar.addMethodCallDelegate(instance, channel: channel)
|
||||
registrar.addApplicationDelegate(instance)
|
||||
}
|
||||
|
||||
// Registers the Flutter engine with the plugins, used by the other Background Flutter engine
|
||||
public static func register(engine: FlutterEngine) {
|
||||
GeneratedPluginRegistrant.register(with: engine)
|
||||
}
|
||||
|
||||
// Registers the task IDs from the system so that we can process them here in this class
|
||||
public static func registerBackgroundProcessing() {
|
||||
|
||||
let processingRegisterd = BGTaskScheduler.shared.register(
|
||||
forTaskWithIdentifier: backgroundProcessingTaskID,
|
||||
using: nil) { task in
|
||||
if task is BGProcessingTask {
|
||||
handleBackgroundProcessing(task: task as! BGProcessingTask)
|
||||
}
|
||||
}
|
||||
|
||||
let fetchRegisterd = BGTaskScheduler.shared.register(
|
||||
forTaskWithIdentifier: backgroundFetchTaskID,
|
||||
using: nil) { task in
|
||||
if task is BGAppRefreshTask {
|
||||
handleBackgroundFetch(task: task as! BGAppRefreshTask)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handles the channel methods from Flutter
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "enable":
|
||||
handleBackgroundEnable(call: call, result: result)
|
||||
break
|
||||
case "configure":
|
||||
handleConfigure(call: call, result: result)
|
||||
break
|
||||
case "disable":
|
||||
handleDisable(call: call, result: result)
|
||||
break
|
||||
case "isEnabled":
|
||||
handleIsEnabled(call: call, result: result)
|
||||
break
|
||||
case "isIgnoringBatteryOptimizations":
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
case "lastBackgroundFetchTime":
|
||||
let defaults = UserDefaults.standard
|
||||
let lastRunTime = defaults.value(forKey: "last_background_fetch_run_time")
|
||||
result(lastRunTime)
|
||||
break
|
||||
case "lastBackgroundProcessingTime":
|
||||
let defaults = UserDefaults.standard
|
||||
let lastRunTime = defaults.value(forKey: "last_background_processing_run_time")
|
||||
result(lastRunTime)
|
||||
break
|
||||
case "numberOfBackgroundProcesses":
|
||||
handleNumberOfProcesses(call: call, result: result)
|
||||
break
|
||||
case "backgroundAppRefreshEnabled":
|
||||
handleBackgroundRefreshStatus(call: call, result: result)
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Called by the flutter code when enabled so that we can turn on the backround services
|
||||
// and save the callback information to communicate on this method channel
|
||||
public func handleBackgroundEnable(call: FlutterMethodCall, result: FlutterResult) {
|
||||
|
||||
// Needs to parse the arguments from the method call
|
||||
guard let args = call.arguments as? Array<Any> else {
|
||||
print("Cannot parse args as array: \(call.arguments)")
|
||||
result(FlutterMethodNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
// Requires 3 arguments in the array
|
||||
guard args.count == 3 else {
|
||||
print("Requires 3 arguments and received \(args.count)")
|
||||
result(FlutterMethodNotImplemented)
|
||||
return
|
||||
}
|
||||
|
||||
// Parses the arguments
|
||||
let callbackHandle = args[0] as? Int64
|
||||
let notificationTitle = args[1] as? String
|
||||
let instant = args[2] as? Bool
|
||||
|
||||
// Write enabled to settings
|
||||
let defaults = UserDefaults.standard
|
||||
|
||||
// We are now enabled, so store this
|
||||
defaults.set(true, forKey: "background_service_enabled")
|
||||
|
||||
// The callback handle is an int64 address to communicate with the main isolate's
|
||||
// entry function
|
||||
defaults.set(callbackHandle, forKey: "callback_handle")
|
||||
|
||||
// This is not used yet and will need to be implemented
|
||||
defaults.set(notificationTitle, forKey: "notification_title")
|
||||
|
||||
// Schedule the background services
|
||||
BackgroundServicePlugin.scheduleBackgroundSync()
|
||||
BackgroundServicePlugin.scheduleBackgroundFetch()
|
||||
|
||||
result(true)
|
||||
}
|
||||
|
||||
// Called by the flutter code at launch to see if the background service is enabled or not
|
||||
func handleIsEnabled(call: FlutterMethodCall, result: FlutterResult) {
|
||||
let defaults = UserDefaults.standard
|
||||
let enabled = defaults.value(forKey: "background_service_enabled") as? Bool
|
||||
|
||||
// False by default
|
||||
result(enabled ?? false)
|
||||
}
|
||||
|
||||
// Called by the Flutter code whenever a change in configuration is set
|
||||
func handleConfigure(call: FlutterMethodCall, result: FlutterResult) {
|
||||
|
||||
// Needs to be able to parse the arguments or else fail
|
||||
guard let args = call.arguments as? Array<Any> else {
|
||||
print("Cannot parse args as array: \(call.arguments)")
|
||||
result(FlutterError())
|
||||
return
|
||||
}
|
||||
|
||||
// Needs to have 4 arguments in the call or else fail
|
||||
guard args.count == 4 else {
|
||||
print("Not enough arguments, 4 required: \(args.count) given")
|
||||
result(FlutterError())
|
||||
return
|
||||
}
|
||||
|
||||
// Parse the arguments from the method call
|
||||
let requireUnmeteredNetwork = args[0] as? Bool
|
||||
let requireCharging = args[1] as? Bool
|
||||
let triggerUpdateDelay = args[2] as? Int
|
||||
let triggerMaxDelay = args[3] as? Int
|
||||
|
||||
// Store the values from the call in the defaults
|
||||
let defaults = UserDefaults.standard
|
||||
defaults.set(requireUnmeteredNetwork, forKey: "require_unmetered_network")
|
||||
defaults.set(requireCharging, forKey: "require_charging")
|
||||
defaults.set(triggerUpdateDelay, forKey: "trigger_update_delay")
|
||||
defaults.set(triggerMaxDelay, forKey: "trigger_max_delay")
|
||||
|
||||
// Cancel the background services and reschedule them
|
||||
BGTaskScheduler.shared.cancelAllTaskRequests()
|
||||
BackgroundServicePlugin.scheduleBackgroundSync()
|
||||
BackgroundServicePlugin.scheduleBackgroundFetch()
|
||||
result(true)
|
||||
}
|
||||
|
||||
// Returns the number of currently scheduled background processes to Flutter, striclty
|
||||
// for debugging
|
||||
func handleNumberOfProcesses(call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
BGTaskScheduler.shared.getPendingTaskRequests { requests in
|
||||
result(requests.count)
|
||||
}
|
||||
}
|
||||
|
||||
// Disables the service, cancels all the task requests
|
||||
func handleDisable(call: FlutterMethodCall, result: FlutterResult) {
|
||||
let defaults = UserDefaults.standard
|
||||
defaults.set(false, forKey: "background_service_enabled")
|
||||
|
||||
BGTaskScheduler.shared.cancelAllTaskRequests()
|
||||
result(true)
|
||||
}
|
||||
|
||||
// Checks the status of the Background App Refresh from the system
|
||||
// Returns true if the service is enabled for Immich, and false otherwise
|
||||
func handleBackgroundRefreshStatus(call: FlutterMethodCall, result: FlutterResult) {
|
||||
switch UIApplication.shared.backgroundRefreshStatus {
|
||||
case .available:
|
||||
result(true)
|
||||
break
|
||||
case .denied:
|
||||
result(false)
|
||||
break
|
||||
case .restricted:
|
||||
result(false)
|
||||
break
|
||||
default:
|
||||
result(false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Schedules a short-running background sync to sync only a few photos
|
||||
static func scheduleBackgroundFetch() {
|
||||
// We will schedule this task to run no matter the charging or wifi requirents from the end user
|
||||
// 1. They can set Background App Refresh to Off / Wi-Fi / Wi-Fi & Cellular Data from Settings
|
||||
// 2. We will check the battery connectivity when we begin running the background activity
|
||||
let backgroundFetch = BGAppRefreshTaskRequest(identifier: BackgroundServicePlugin.backgroundFetchTaskID)
|
||||
|
||||
// Use 5 minutes from now as earliest begin date
|
||||
backgroundFetch.earliestBeginDate = Date(timeIntervalSinceNow: 5 * 60)
|
||||
|
||||
do {
|
||||
try BGTaskScheduler.shared.submit(backgroundFetch)
|
||||
} catch {
|
||||
print("Could not schedule the background task \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
// Schedules a long-running background sync for syncing all of the photos
|
||||
static func scheduleBackgroundSync() {
|
||||
let backgroundProcessing = BGProcessingTaskRequest(identifier: BackgroundServicePlugin.backgroundProcessingTaskID)
|
||||
|
||||
// We need the values for requiring charging
|
||||
let defaults = UserDefaults.standard
|
||||
let requireCharging = defaults.value(forKey: "require_charging") as? Bool
|
||||
|
||||
// Always require network connectivity, and set the require charging from the above
|
||||
backgroundProcessing.requiresNetworkConnectivity = true
|
||||
backgroundProcessing.requiresExternalPower = requireCharging ?? true
|
||||
|
||||
// Use 15 minutes from now as earliest begin date
|
||||
backgroundProcessing.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
|
||||
|
||||
do {
|
||||
// Submit the task to the scheduler
|
||||
try BGTaskScheduler.shared.submit(backgroundProcessing)
|
||||
} catch {
|
||||
print("Could not schedule the background task \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
// This function runs when the system kicks off the BGAppRefreshTask from the Background Task Scheduler
|
||||
static func handleBackgroundFetch(task: BGAppRefreshTask) {
|
||||
// Schedule the next sync task so we can run this again later
|
||||
scheduleBackgroundFetch()
|
||||
|
||||
// Log the time of last background processing to now
|
||||
let defaults = UserDefaults.standard
|
||||
defaults.set(Date().timeIntervalSince1970, forKey: "last_background_fetch_run_time")
|
||||
|
||||
// If we have required charging, we should check the charging status
|
||||
let requireCharging = defaults.value(forKey: "require_charging") as? Bool
|
||||
if (requireCharging ?? false) {
|
||||
UIDevice.current.isBatteryMonitoringEnabled = true
|
||||
if (UIDevice.current.batteryState == .unplugged) {
|
||||
// The device is unplugged and we have required charging
|
||||
// Therefore, we will simply complete the task without
|
||||
// running it.
|
||||
task.setTaskCompleted(success: true)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Schedule the next sync task so we can run this again later
|
||||
scheduleBackgroundFetch()
|
||||
|
||||
// The background sync task should only run for 20 seconds at most
|
||||
BackgroundServicePlugin.runBackgroundSync(task, maxSeconds: 20)
|
||||
}
|
||||
|
||||
// This function runs when the system kicks off the BGProcessingTask from the Background Task Scheduler
|
||||
static func handleBackgroundProcessing(task: BGProcessingTask) {
|
||||
// Schedule the next sync task so we run this again later
|
||||
scheduleBackgroundSync()
|
||||
|
||||
// Log the time of last background processing to now
|
||||
let defaults = UserDefaults.standard
|
||||
defaults.set(Date().timeIntervalSince1970, forKey: "last_background_processing_run_time")
|
||||
|
||||
// We won't specify a max time for the background sync service, so this can run for longer
|
||||
BackgroundServicePlugin.runBackgroundSync(task, maxSeconds: nil)
|
||||
}
|
||||
|
||||
// This is a synchronous function which uses a semaphore to run the background sync worker's run
|
||||
// function, which will create a background Isolate and communicate with the Flutter code to back
|
||||
// up the assets. When it completes, we signal the semaphore and complete the execution allowing the
|
||||
// control to pass back to the caller synchronously
|
||||
static func runBackgroundSync(_ task: BGTask, maxSeconds: Int?) {
|
||||
|
||||
let semaphore = DispatchSemaphore(value: 0)
|
||||
DispatchQueue.main.async {
|
||||
let backgroundWorker = BackgroundSyncWorker { _ in
|
||||
semaphore.signal()
|
||||
}
|
||||
task.expirationHandler = {
|
||||
backgroundWorker.cancel()
|
||||
task.setTaskCompleted(success: true)
|
||||
}
|
||||
|
||||
backgroundWorker.run(maxSeconds: maxSeconds)
|
||||
task.setTaskCompleted(success: true)
|
||||
}
|
||||
semaphore.wait()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
271
mobile/ios/Runner/BackgroundSync/BackgroundSyncWorker.swift
Normal file
@@ -0,0 +1,271 @@
|
||||
//
|
||||
// BackgroundSyncProcessing.swift
|
||||
// Runner
|
||||
//
|
||||
// Created by Marty Fuhry on 2/6/23.
|
||||
//
|
||||
// Credit to https://github.com/fluttercommunity/flutter_workmanager/blob/main/ios/Classes/BackgroundWorker.swift
|
||||
|
||||
import Foundation
|
||||
import Flutter
|
||||
import BackgroundTasks
|
||||
|
||||
// The background worker which creates a new Flutter VM, communicates with it
|
||||
// to run the backup job, and then finishes execution and calls back to its callback
|
||||
// handler
|
||||
class BackgroundSyncWorker {
|
||||
|
||||
// The Flutter engine we create for background execution.
|
||||
// This is not the main Flutter engine which shows the UI,
|
||||
// this is a brand new isolate created and managed in this code
|
||||
// here. It does not share memory with the main
|
||||
// Flutter engine which shows the UI.
|
||||
// It needs to be started up, registered, and torn down here
|
||||
let engine: FlutterEngine? = FlutterEngine(
|
||||
name: "BackgroundImmich"
|
||||
)
|
||||
|
||||
let notificationId = "com.alextran.immich/backgroundNotifications"
|
||||
// The background message passing channel
|
||||
var channel: FlutterMethodChannel?
|
||||
|
||||
var completionHandler: (UIBackgroundFetchResult) -> Void
|
||||
let taskSessionStart = Date()
|
||||
|
||||
// We need the completion handler to tell the system when we are done running
|
||||
init(_ completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
|
||||
|
||||
// This is the background message passing channel to be used with the background engine
|
||||
// created here in this platform code
|
||||
self.channel = FlutterMethodChannel(
|
||||
name: "immich/backgroundChannel",
|
||||
binaryMessenger: engine!.binaryMessenger
|
||||
)
|
||||
self.completionHandler = completionHandler
|
||||
}
|
||||
|
||||
// Handles all of the messages from the Flutter VM called into this platform code
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
switch call.method {
|
||||
case "initialized":
|
||||
// Initialize tells us that we can now call into the Flutter VM to tell it to begin the update
|
||||
self.channel?.invokeMethod(
|
||||
"backgroundProcessing",
|
||||
arguments: nil,
|
||||
result: { flutterResult in
|
||||
|
||||
// This is the result we send back to the BGTaskScheduler to let it know whether we'll need more time later or
|
||||
// if this execution failed
|
||||
let result: UIBackgroundFetchResult = (flutterResult as? Bool ?? false) ? .newData : .failed
|
||||
|
||||
// Show the task duration
|
||||
let taskSessionCompleter = Date()
|
||||
let taskDuration = taskSessionCompleter.timeIntervalSince(self.taskSessionStart)
|
||||
print("[\(String(describing: self))] \(#function) -> performBackgroundRequest.\(result) (finished in \(taskDuration) seconds)")
|
||||
|
||||
// Complete the execution
|
||||
self.complete(result)
|
||||
})
|
||||
break
|
||||
case "updateNotification":
|
||||
let handled = self.handleNotification(call)
|
||||
result(handled)
|
||||
break
|
||||
case "showError":
|
||||
let handled = self.handleError(call)
|
||||
result(handled)
|
||||
break
|
||||
case "clearErrorNotifications":
|
||||
self.handleClearErrorNotifications()
|
||||
result(true)
|
||||
break
|
||||
case "hasContentChanged":
|
||||
// This is only called for Android, but we provide an implementation here
|
||||
// telling Flutter that we don't have any information about whether the gallery
|
||||
// contents have changed or not, so we can just say "no, they've not changed"
|
||||
result(false)
|
||||
break
|
||||
default:
|
||||
result(FlutterError())
|
||||
self.complete(UIBackgroundFetchResult.failed)
|
||||
}
|
||||
}
|
||||
|
||||
// Runs the background sync by starting up a new isolate and handling the calls
|
||||
// until it completes
|
||||
public func run(maxSeconds: Int?) {
|
||||
// We need the callback handle to start up the Flutter VM from the entry point
|
||||
let defaults = UserDefaults.standard
|
||||
guard let callbackHandle = defaults.value(forKey: "callback_handle") as? Int64 else {
|
||||
// Can't find the callback handle, this is fatal
|
||||
complete(UIBackgroundFetchResult.failed)
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
// Use the provided callbackHandle to get the callback function
|
||||
guard let callback = FlutterCallbackCache.lookupCallbackInformation(callbackHandle) else {
|
||||
// We need this callback or else this is fatal
|
||||
complete(UIBackgroundFetchResult.failed)
|
||||
return
|
||||
}
|
||||
|
||||
// Sanity check for the engine existing
|
||||
if engine == nil {
|
||||
complete(UIBackgroundFetchResult.failed)
|
||||
return
|
||||
}
|
||||
|
||||
// Run the engine
|
||||
let isRunning = engine!.run(
|
||||
withEntrypoint: callback.callbackName,
|
||||
libraryURI: callback.callbackLibraryPath
|
||||
)
|
||||
|
||||
// If this engine isn't running, this is fatal
|
||||
if !isRunning {
|
||||
complete(UIBackgroundFetchResult.failed)
|
||||
return
|
||||
}
|
||||
|
||||
// If we have a timer, we need to start the timer to cancel ourselves
|
||||
// so that we don't run longer than the provided maxSeconds
|
||||
// After maxSeconds has elapsed, we will invoke "systemStop"
|
||||
if maxSeconds != nil {
|
||||
// Schedule a non-repeating timer to run after maxSeconds
|
||||
let timer = Timer.scheduledTimer(withTimeInterval: TimeInterval(maxSeconds!),
|
||||
repeats: false) { timer in
|
||||
// The callback invalidates the timer and stops execution
|
||||
timer.invalidate()
|
||||
|
||||
// If the channel is already deallocated, we don't need to do anything
|
||||
if self.channel == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Tell the Flutter VM to stop backing up now
|
||||
self.channel?.invokeMethod(
|
||||
"systemStop",
|
||||
arguments: nil,
|
||||
result: nil)
|
||||
|
||||
// Complete the execution
|
||||
self.complete(UIBackgroundFetchResult.newData)
|
||||
}
|
||||
}
|
||||
|
||||
// Set the handle function to the channel message handler
|
||||
self.channel?.setMethodCallHandler(handle)
|
||||
|
||||
// Register this to get access to the plugins on the platform channel
|
||||
BackgroundServicePlugin.flutterPluginRegistrantCallback?(engine!)
|
||||
}
|
||||
|
||||
// Cancels execution of this task, used by the system's task expiration handler
|
||||
// which is called shortly before execution is about to expire
|
||||
public func cancel() {
|
||||
// If the channel is already deallocated, we don't need to do anything
|
||||
if self.channel == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Tell the Flutter VM to stop backing up now
|
||||
self.channel?.invokeMethod(
|
||||
"systemStop",
|
||||
arguments: nil,
|
||||
result: nil)
|
||||
|
||||
// Complete the execution
|
||||
self.complete(UIBackgroundFetchResult.newData)
|
||||
}
|
||||
|
||||
// Completes the execution, destroys the engine, and sends a completion to our callback completionHandler
|
||||
private func complete(_ fetchResult: UIBackgroundFetchResult) {
|
||||
engine?.destroyContext()
|
||||
channel = nil
|
||||
completionHandler(fetchResult)
|
||||
}
|
||||
|
||||
private func handleNotification(_ call: FlutterMethodCall) -> Bool {
|
||||
|
||||
// Parse the arguments as an array list
|
||||
guard let args = call.arguments as? Array<Any> else {
|
||||
print("Failed to parse \(call.arguments) as array")
|
||||
return false;
|
||||
}
|
||||
|
||||
// Requires 7 arguments passed or else fail
|
||||
guard args.count == 7 else {
|
||||
print("Needs 7 arguments, but was only passed \(args.count)")
|
||||
return false
|
||||
}
|
||||
|
||||
// Parse the arguments to send the notification update
|
||||
let title = args[0] as? String
|
||||
let content = args[1] as? String
|
||||
let progress = args[2] as? Int
|
||||
let maximum = args[3] as? Int
|
||||
let indeterminate = args[4] as? Bool
|
||||
let isDetail = args[5] as? Bool
|
||||
let onlyIfForeground = args[6] as? Bool
|
||||
|
||||
// Build the notification
|
||||
let notificationContent = UNMutableNotificationContent()
|
||||
notificationContent.body = content ?? "Uploading..."
|
||||
notificationContent.title = title ?? "Immich"
|
||||
|
||||
// Add it to the Notification center
|
||||
let notification = UNNotificationRequest(
|
||||
identifier: notificationId,
|
||||
content: notificationContent,
|
||||
trigger: nil
|
||||
)
|
||||
let center = UNUserNotificationCenter.current()
|
||||
center.add(notification) { (error: Error?) in
|
||||
if let theError = error {
|
||||
print("Error showing notifications: \(theError)")
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private func handleError(_ call: FlutterMethodCall) -> Bool {
|
||||
// Parse the arguments as an array list
|
||||
guard let args = call.arguments as? Array<Any> else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Requires 7 arguments passed or else fail
|
||||
guard args.count == 3 else {
|
||||
return false
|
||||
}
|
||||
|
||||
let title = args[0] as? String
|
||||
let content = args[1] as? String
|
||||
let individualTag = args[2] as? String
|
||||
|
||||
// Build the notification
|
||||
let notificationContent = UNMutableNotificationContent()
|
||||
notificationContent.body = content ?? "Error running the backup job."
|
||||
notificationContent.title = title ?? "Immich"
|
||||
|
||||
// Add it to the Notification center
|
||||
let notification = UNNotificationRequest(
|
||||
identifier: notificationId,
|
||||
content: notificationContent,
|
||||
trigger: nil
|
||||
)
|
||||
let center = UNUserNotificationCenter.current()
|
||||
center.add(notification)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private func handleClearErrorNotifications() {
|
||||
let center = UNUserNotificationCenter.current()
|
||||
center.removeDeliveredNotifications(withIdentifiers: [notificationId])
|
||||
center.removePendingNotificationRequests(withIdentifiers: [notificationId])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,101 +1,113 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Immich</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>immich_mobile</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>$(FLUTTER_BUILD_NAME)</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>MGLMapboxMetricsEnabledSettingShownInApp</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
<string>Enable location setting to show position of assets on map</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>Enable location setting to show position of assets on map</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>We need to manage backup your photos album</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>We need to manage backup your photos album</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>We need to access the camera to let you take beautiful video using this app</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>We need to access the microphone to let you take beautiful video using this app</string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true/>
|
||||
<key>io.flutter.embedded_views_preview</key>
|
||||
<true/>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>https</string>
|
||||
</array>
|
||||
<key>CFBundleLocalizations</key>
|
||||
<array>
|
||||
<string>cs</string>
|
||||
<string>da</string>
|
||||
<string>de</string>
|
||||
<string>en</string>
|
||||
<string>es</string>
|
||||
<string>fi</string>
|
||||
<string>fr</string>
|
||||
<string>it</string>
|
||||
<string>ja</string>
|
||||
<string>ko</string>
|
||||
<string>nl</string>
|
||||
<string>pl</string>
|
||||
<string>pt</string>
|
||||
<string>ru</string>
|
||||
<string>sk</string>
|
||||
<string>zh</string>
|
||||
</array>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>FLTEnableImpeller</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
<dict>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>app.alextran.immich.backgroundFetch</string>
|
||||
<string>app.alextran.immich.backgroundProcessing</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true />
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Immich</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleLocalizations</key>
|
||||
<array>
|
||||
<string>cs</string>
|
||||
<string>da</string>
|
||||
<string>de</string>
|
||||
<string>en</string>
|
||||
<string>es</string>
|
||||
<string>fi</string>
|
||||
<string>fr</string>
|
||||
<string>it</string>
|
||||
<string>ja</string>
|
||||
<string>ko</string>
|
||||
<string>nl</string>
|
||||
<string>pl</string>
|
||||
<string>pt</string>
|
||||
<string>ru</string>
|
||||
<string>se</string>
|
||||
<string>sk</string>
|
||||
<string>zh</string>
|
||||
<string>no</string>
|
||||
</array>
|
||||
<key>CFBundleName</key>
|
||||
<string>immich_mobile</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.50.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>88</string>
|
||||
<key>FLTEnableImpeller</key>
|
||||
<true />
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false />
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>https</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true />
|
||||
<key>MGLMapboxMetricsEnabledSettingShownInApp</key>
|
||||
<true />
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true />
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>We need to access the camera to let you take beautiful video using this app</string>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
<string>Enable location setting to show position of assets on map</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>Enable location setting to show position of assets on map</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>We need to access the microphone to let you take beautiful video using this app</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>We need to manage backup your photos album</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>We need to manage backup your photos album</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true />
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>fetch</string>
|
||||
<string>processing</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false />
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<true />
|
||||
<key>io.flutter.embedded_views_preview</key>
|
||||
<true />
|
||||
</dict>
|
||||
</plist>
|
||||
5
mobile/ios/Runner/Runner.entitlements
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
||||
@@ -19,7 +19,7 @@ platform :ios do
|
||||
desc "iOS Beta"
|
||||
lane :beta do
|
||||
increment_version_number(
|
||||
version_number: "1.47.2"
|
||||
version_number: "1.51.1"
|
||||
)
|
||||
increment_build_number(
|
||||
build_number: latest_testflight_build_number + 1,
|
||||
|
||||
@@ -5,32 +5,34 @@
|
||||
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000283">
|
||||
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.000301">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="1: increment_version_number" time="2.755864">
|
||||
<testcase classname="fastlane.lanes" name="1: increment_version_number" time="1.613972">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="2: latest_testflight_build_number" time="7.319767">
|
||||
<testcase classname="fastlane.lanes" name="2: latest_testflight_build_number" time="4.887872">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="3: increment_build_number" time="1.376562">
|
||||
<testcase classname="fastlane.lanes" name="3: increment_build_number" time="1.53884">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="4: build_app" time="105.396514">
|
||||
<testcase classname="fastlane.lanes" name="4: build_app" time="64.096001">
|
||||
|
||||
</testcase>
|
||||
|
||||
|
||||
<testcase classname="fastlane.lanes" name="5: upload_to_testflight" time="86.092896">
|
||||
<testcase classname="fastlane.lanes" name="5: upload_to_testflight" time="6.017821">
|
||||
|
||||
<failure message="/usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/actions/actions_helper.rb:67:in `execute_action' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/runner.rb:255:in `block in execute_action' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/runner.rb:229:in `chdir' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/runner.rb:229:in `execute_action' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/runner.rb:157:in `trigger_action_by_name' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/fast_file.rb:159:in `method_missing' Fastfile:30:in `block (2 levels) in parsing_binding' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/lane.rb:33:in `call' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/runner.rb:49:in `block in execute' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/runner.rb:45:in `chdir' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/runner.rb:45:in `execute' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/lane_manager.rb:47:in `cruise_lane' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/command_line_handler.rb:36:in `handle' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/commands_generator.rb:110:in `block (2 levels) in run' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/commander-4.6.0/lib/commander/command.rb:187:in `call' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/commander-4.6.0/lib/commander/command.rb:157:in `run' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/commander-4.6.0/lib/commander/runner.rb:444:in `run_active_command' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane_core/lib/fastlane_core/ui/fastlane_runner.rb:124:in `run!' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/commander-4.6.0/lib/commander/delegates.rb:18:in `run!' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/commands_generator.rb:354:in `run' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/commands_generator.rb:43:in `start' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/fastlane/lib/fastlane/cli_tools_distributor.rb:123:in `take_off' /usr/local/Cellar/fastlane/2.212.1/libexec/gems/fastlane-2.212.1/bin/fastlane:23:in `<top (required)>' /usr/local/Cellar/fastlane/2.212.1/libexec/bin/fastlane:25:in `load' /usr/local/Cellar/fastlane/2.212.1/libexec/bin/fastlane:25:in `<main>' Error uploading ipa file: [Application Loader Error Output]: Error uploading '/var/folders/lp/myp2frzj00g93mcbnz6cd8900000gn/T/279a4279-ff7e-48d3-b6fd-1ee020a5b0a9.ipa'.
|
||||
[Application Loader Error Output]: Unable to upload archive. Failed to get authorization for username 'alex.tran1502@gmail.com' and password. (
|
||||
[Application Loader Error Output]: The call to the altool completed with a non-zero exit status: 1. This indicates a failure." />
|
||||
|
||||
|
||||
@@ -17,8 +17,10 @@ const List<Locale> locales = [
|
||||
Locale('pl', 'PL'),
|
||||
Locale('pt', 'PR'),
|
||||
Locale('ru', 'RU'),
|
||||
Locale('sv', 'SE'),
|
||||
Locale('sk', 'SK'),
|
||||
Locale('zh', 'CN'),
|
||||
Locale('no', 'NO'),
|
||||
];
|
||||
|
||||
const String translationsPath = 'assets/i18n';
|
||||
|
||||
@@ -9,15 +9,24 @@ import 'package:hive_flutter/hive_flutter.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/constants/locales.dart';
|
||||
import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
|
||||
import 'package:immich_mobile/modules/backup/models/backup_album.model.dart';
|
||||
import 'package:immich_mobile/modules/backup/models/duplicated_asset.model.dart';
|
||||
import 'package:immich_mobile/modules/backup/models/hive_backup_albums.model.dart';
|
||||
import 'package:immich_mobile/modules/backup/models/hive_duplicated_assets.model.dart';
|
||||
import 'package:immich_mobile/modules/backup/providers/backup.provider.dart';
|
||||
import 'package:immich_mobile/modules/backup/providers/ios_background_settings.provider.dart';
|
||||
import 'package:immich_mobile/modules/login/models/hive_saved_login_info.model.dart';
|
||||
import 'package:immich_mobile/modules/login/providers/authentication.provider.dart';
|
||||
import 'package:immich_mobile/modules/onboarding/providers/gallery_permission.provider.dart';
|
||||
import 'package:immich_mobile/modules/settings/providers/notification_permission.provider.dart';
|
||||
import 'package:immich_mobile/routing/router.dart';
|
||||
import 'package:immich_mobile/routing/tab_navigation_observer.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/models/exif_info.dart';
|
||||
import 'package:immich_mobile/shared/models/immich_logger_message.model.dart';
|
||||
import 'package:immich_mobile/shared/models/store.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/providers/app_state.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/asset.provider.dart';
|
||||
import 'package:immich_mobile/shared/providers/db.provider.dart';
|
||||
@@ -30,13 +39,16 @@ import 'package:immich_mobile/shared/views/version_announcement_overlay.dart';
|
||||
import 'package:immich_mobile/utils/immich_app_theme.dart';
|
||||
import 'package:immich_mobile/utils/migration.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'constants/hive_box.dart';
|
||||
|
||||
void main() async {
|
||||
await initApp();
|
||||
final db = await loadDb();
|
||||
await migrateHiveToStoreIfNecessary();
|
||||
await migrateJsonCacheIfNecessary();
|
||||
runApp(getMainWidget(db));
|
||||
}
|
||||
|
||||
@@ -47,10 +59,6 @@ Future<void> openBoxes() async {
|
||||
Hive.openBox<HiveSavedLoginInfo>(hiveLoginInfoBox),
|
||||
Hive.openBox(hiveGithubReleaseInfoBox),
|
||||
Hive.openBox(userSettingInfoBox),
|
||||
if (!Platform.isAndroid) Hive.openBox<HiveBackupAlbums>(hiveBackupInfoBox),
|
||||
if (!Platform.isAndroid)
|
||||
Hive.openBox<HiveDuplicatedAssets>(duplicatedAssetsBox),
|
||||
if (!Platform.isAndroid) Hive.openBox(backgroundBackupInfoBox),
|
||||
EasyLocalization.ensureInitialized(),
|
||||
]);
|
||||
}
|
||||
@@ -75,12 +83,32 @@ Future<void> initApp() async {
|
||||
|
||||
// Initialize Immich Logger Service
|
||||
ImmichLogger().init();
|
||||
|
||||
var log = Logger("ImmichErrorLogger");
|
||||
|
||||
FlutterError.onError = (details) {
|
||||
FlutterError.presentError(details);
|
||||
log.severe(details.toString(), details, details.stack);
|
||||
};
|
||||
|
||||
PlatformDispatcher.instance.onError = (error, stack) {
|
||||
log.severe(error.toString(), error, stack);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
Future<Isar> loadDb() async {
|
||||
final dir = await getApplicationDocumentsDirectory();
|
||||
Isar db = await Isar.open(
|
||||
[StoreValueSchema],
|
||||
[
|
||||
StoreValueSchema,
|
||||
ExifInfoSchema,
|
||||
AssetSchema,
|
||||
AlbumSchema,
|
||||
UserSchema,
|
||||
BackupAlbumSchema,
|
||||
DuplicatedAssetSchema,
|
||||
],
|
||||
directory: dir.path,
|
||||
maxSizeMiB: 256,
|
||||
);
|
||||
@@ -118,8 +146,10 @@ class ImmichAppState extends ConsumerState<ImmichApp>
|
||||
ref.watch(appStateProvider.notifier).state = AppStateEnum.resumed;
|
||||
|
||||
var isAuthenticated = ref.watch(authenticationProvider).isAuthenticated;
|
||||
final permission = ref.watch(galleryPermissionNotifier);
|
||||
|
||||
if (isAuthenticated) {
|
||||
// Needs to be logged in and have gallery permissions
|
||||
if (isAuthenticated && (permission.isGranted || permission.isLimited)) {
|
||||
ref.read(backupProvider.notifier).resumeBackup();
|
||||
ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
|
||||
ref.watch(assetProvider.notifier).getAllAsset();
|
||||
@@ -130,6 +160,15 @@ class ImmichAppState extends ConsumerState<ImmichApp>
|
||||
|
||||
ref.watch(releaseInfoProvider.notifier).checkGithubReleaseInfo();
|
||||
|
||||
ref
|
||||
.watch(notificationPermissionProvider.notifier)
|
||||
.getNotificationPermission();
|
||||
ref
|
||||
.watch(galleryPermissionNotifier.notifier)
|
||||
.getGalleryPermissionStatus();
|
||||
|
||||
ref.read(iOSBackgroundSettingsProvider.notifier).refresh();
|
||||
|
||||
break;
|
||||
|
||||
case AppLifecycleState.inactive:
|
||||
@@ -178,6 +217,9 @@ class ImmichAppState extends ConsumerState<ImmichApp>
|
||||
ref.watch(releaseInfoProvider.notifier).checkGithubReleaseInfo();
|
||||
|
||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||
SystemChrome.setSystemUIOverlayStyle(
|
||||
const SystemUiOverlayStyle(systemNavigationBarColor: Colors.transparent),
|
||||
);
|
||||
|
||||
return MaterialApp(
|
||||
localizationsDelegates: context.localizationDelegates,
|
||||
|
||||
@@ -1,37 +1,43 @@
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:immich_mobile/modules/album/services/album.service.dart';
|
||||
import 'package:immich_mobile/modules/album/services/album_cache.service.dart';
|
||||
import 'package:immich_mobile/shared/models/asset.dart';
|
||||
import 'package:immich_mobile/shared/models/album.dart';
|
||||
import 'package:immich_mobile/shared/models/store.dart';
|
||||
import 'package:immich_mobile/shared/models/user.dart';
|
||||
import 'package:immich_mobile/shared/providers/db.provider.dart';
|
||||
import 'package:isar/isar.dart';
|
||||
|
||||
class AlbumNotifier extends StateNotifier<List<Album>> {
|
||||
AlbumNotifier(this._albumService, this._albumCacheService) : super([]);
|
||||
AlbumNotifier(this._albumService, this._db) : super([]);
|
||||
final AlbumService _albumService;
|
||||
final AlbumCacheService _albumCacheService;
|
||||
|
||||
void _cacheState() {
|
||||
_albumCacheService.put(state);
|
||||
}
|
||||
final Isar _db;
|
||||
|
||||
Future<void> getAllAlbums() async {
|
||||
if (await _albumCacheService.isValid() && state.isEmpty) {
|
||||
final albums = await _albumCacheService.get();
|
||||
if (albums != null) {
|
||||
state = albums;
|
||||
}
|
||||
}
|
||||
|
||||
final albums = await _albumService.getAlbums(isShared: false);
|
||||
|
||||
if (albums != null) {
|
||||
final User me = Store.get(StoreKey.currentUser);
|
||||
List<Album> albums = await _db.albums
|
||||
.filter()
|
||||
.owner((q) => q.isarIdEqualTo(me.isarId))
|
||||
.findAll();
|
||||
if (!const ListEquality().equals(albums, state)) {
|
||||
state = albums;
|
||||
}
|
||||
await Future.wait([
|
||||
_albumService.refreshDeviceAlbums(),
|
||||
_albumService.refreshRemoteAlbums(isShared: false),
|
||||
]);
|
||||
albums = await _db.albums
|
||||
.filter()
|
||||
.owner((q) => q.isarIdEqualTo(me.isarId))
|
||||
.findAll();
|
||||
if (!const ListEquality().equals(albums, state)) {
|
||||
state = albums;
|
||||
_cacheState();
|
||||
}
|
||||
}
|
||||
|
||||
void deleteAlbum(Album album) {
|
||||
Future<bool> deleteAlbum(Album album) async {
|
||||
state = state.where((a) => a.id != album.id).toList();
|
||||
_cacheState();
|
||||
return _albumService.deleteAlbum(album);
|
||||
}
|
||||
|
||||
Future<Album?> createAlbum(
|
||||
@@ -39,20 +45,16 @@ class AlbumNotifier extends StateNotifier<List<Album>> {
|
||||
Set<Asset> assets,
|
||||
) async {
|
||||
Album? album = await _albumService.createAlbum(albumTitle, assets, []);
|
||||
|
||||
if (album != null) {
|
||||
state = [...state, album];
|
||||
_cacheState();
|
||||
|
||||
return album;
|
||||
}
|
||||
return null;
|
||||
return album;
|
||||
}
|
||||
}
|
||||
|
||||
final albumProvider = StateNotifierProvider<AlbumNotifier, List<Album>>((ref) {
|
||||
return AlbumNotifier(
|
||||
ref.watch(albumServiceProvider),
|
||||
ref.watch(albumCacheServiceProvider),
|
||||
ref.watch(dbProvider),
|
||||
);
|
||||
});
|
||||
|
||||