Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d261fa0fc9 | |||
| 352298e4ca | |||
| 7dc9cb121f | |||
| ca8a6e5f95 | |||
| 1086623457 | |||
| a2b25b7a74 | |||
| 950f268cb0 | |||
| 5ece0e5e56 | |||
| 00ce6354f0 | |||
| 9b938d8954 | |||
| 9db0baa42b |
+1
-1
@@ -1 +1 @@
|
|||||||
22.19.0
|
22.20.0
|
||||||
|
|||||||
@@ -49,14 +49,14 @@
|
|||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
- [Documentation](https://immich.app/docs)
|
- [Documentation](https://docs.immich.app/)
|
||||||
- [About](https://immich.app/docs/overview/introduction)
|
- [About](https://docs.immich.app/overview/introduction)
|
||||||
- [Installation](https://immich.app/docs/install/requirements)
|
- [Installation](https://docs.immich.app/install/requirements)
|
||||||
- [Roadmap](https://immich.app/roadmap)
|
- [Roadmap](https://immich.app/roadmap)
|
||||||
- [Demo](#demo)
|
- [Demo](#demo)
|
||||||
- [Features](#features)
|
- [Features](#features)
|
||||||
- [Translations](https://immich.app/docs/developer/translations)
|
- [Translations](https://docs.immich.app/developer/translations)
|
||||||
- [Contributing](https://immich.app/docs/overview/support-the-project)
|
- [Contributing](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ Access the demo [here](https://demo.immich.app). For the mobile app, you can use
|
|||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
|
|
||||||
Read more about translations [here](https://immich.app/docs/developer/translations).
|
Read more about translations [here](https://docs.immich.app/developer/translations).
|
||||||
|
|
||||||
<a href="https://hosted.weblate.org/engage/immich/">
|
<a href="https://hosted.weblate.org/engage/immich/">
|
||||||
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Translation status" />
|
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Translation status" />
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
22.19.0
|
22.20.0
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
A command-line interface for interfacing with the self-hosted photo manager [Immich](https://immich.app/).
|
A command-line interface for interfacing with the self-hosted photo manager [Immich](https://immich.app/).
|
||||||
|
|
||||||
Please see the [Immich CLI documentation](https://immich.app/docs/features/command-line-interface).
|
Please see the [Immich CLI documentation](https://docs.immich.app/features/command-line-interface).
|
||||||
|
|
||||||
# For developers
|
# For developers
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -68,6 +68,6 @@
|
|||||||
"micromatch": "^4.0.8"
|
"micromatch": "^4.0.8"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.19.0"
|
"node": "22.20.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# WARNING: To install Immich, follow our guide: https://immich.app/docs/install/docker-compose
|
# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose
|
||||||
#
|
#
|
||||||
# Make sure to use the docker-compose.yml of the current release:
|
# Make sure to use the docker-compose.yml of the current release:
|
||||||
#
|
#
|
||||||
@@ -8,8 +8,8 @@
|
|||||||
# The compose file on main may not be compatible with the latest release.
|
# The compose file on main may not be compatible with the latest release.
|
||||||
|
|
||||||
# For development see:
|
# For development see:
|
||||||
# - https://immich.app/docs/developer/setup
|
# - https://docs.immich.app/developer/setup
|
||||||
# - https://immich.app/docs/developer/troubleshooting
|
# - https://docs.immich.app/developer/troubleshooting
|
||||||
|
|
||||||
name: immich-dev
|
name: immich-dev
|
||||||
|
|
||||||
@@ -55,8 +55,8 @@ services:
|
|||||||
IMMICH_BUILD_IMAGE_URL: https://github.com/immich-app/immich/pkgs/container/immich-server
|
IMMICH_BUILD_IMAGE_URL: https://github.com/immich-app/immich/pkgs/container/immich-server
|
||||||
IMMICH_THIRD_PARTY_SOURCE_URL: https://github.com/immich-app/immich/
|
IMMICH_THIRD_PARTY_SOURCE_URL: https://github.com/immich-app/immich/
|
||||||
IMMICH_THIRD_PARTY_BUG_FEATURE_URL: https://github.com/immich-app/immich/issues
|
IMMICH_THIRD_PARTY_BUG_FEATURE_URL: https://github.com/immich-app/immich/issues
|
||||||
IMMICH_THIRD_PARTY_DOCUMENTATION_URL: https://immich.app/docs
|
IMMICH_THIRD_PARTY_DOCUMENTATION_URL: https://docs.immich.app
|
||||||
IMMICH_THIRD_PARTY_SUPPORT_URL: https://immich.app/docs/community-guides
|
IMMICH_THIRD_PARTY_SUPPORT_URL: https://docs.immich.app/community-guides
|
||||||
ulimits:
|
ulimits:
|
||||||
nofile:
|
nofile:
|
||||||
soft: 1048576
|
soft: 1048576
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# WARNING: To install Immich, follow our guide: https://immich.app/docs/install/docker-compose
|
# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose
|
||||||
#
|
#
|
||||||
# Make sure to use the docker-compose.yml of the current release:
|
# Make sure to use the docker-compose.yml of the current release:
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#
|
#
|
||||||
# WARNING: To install Immich, follow our guide: https://immich.app/docs/install/docker-compose
|
# WARNING: To install Immich, follow our guide: https://docs.immich.app/install/docker-compose
|
||||||
#
|
#
|
||||||
# Make sure to use the docker-compose.yml of the current release:
|
# Make sure to use the docker-compose.yml of the current release:
|
||||||
#
|
#
|
||||||
@@ -36,7 +36,7 @@ services:
|
|||||||
# For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag.
|
# For hardware acceleration, add one of -[armnn, cuda, rocm, openvino, rknn] to the image tag.
|
||||||
# Example tag: ${IMMICH_VERSION:-release}-cuda
|
# Example tag: ${IMMICH_VERSION:-release}-cuda
|
||||||
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
|
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
|
||||||
# extends: # uncomment this section for hardware acceleration - see https://immich.app/docs/features/ml-hardware-acceleration
|
# extends: # uncomment this section for hardware acceleration - see https://docs.immich.app/features/ml-hardware-acceleration
|
||||||
# file: hwaccel.ml.yml
|
# file: hwaccel.ml.yml
|
||||||
# service: cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable
|
# service: cpu # set to one of [armnn, cuda, rocm, openvino, openvino-wsl, rknn] for accelerated inference - use the `-wsl` version for WSL2 where applicable
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
# You can find documentation for all the supported env variables at https://immich.app/docs/install/environment-variables
|
# You can find documentation for all the supported env variables at https://docs.immich.app/install/environment-variables
|
||||||
|
|
||||||
# The location where your uploaded files are stored
|
# The location where your uploaded files are stored
|
||||||
UPLOAD_LOCATION=./library
|
UPLOAD_LOCATION=./library
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
# you can inline the config for a backend by copying its contents
|
# you can inline the config for a backend by copying its contents
|
||||||
# into the immich-machine-learning service in the docker-compose.yml file.
|
# into the immich-machine-learning service in the docker-compose.yml file.
|
||||||
|
|
||||||
# See https://immich.app/docs/features/ml-hardware-acceleration for info on usage.
|
# See https://docs.immich.app/features/ml-hardware-acceleration for info on usage.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
armnn:
|
armnn:
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
# you can inline the config for a backend by copying its contents
|
# you can inline the config for a backend by copying its contents
|
||||||
# into the immich-microservices service in the docker-compose.yml file.
|
# into the immich-microservices service in the docker-compose.yml file.
|
||||||
|
|
||||||
# See https://immich.app/docs/features/hardware-transcoding for more info on using hardware transcoding.
|
# See https://docs.immich.app/features/hardware-transcoding for more info on using hardware transcoding.
|
||||||
|
|
||||||
services:
|
services:
|
||||||
cpu: {}
|
cpu: {}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
22.19.0
|
22.20.0
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 65
|
||||||
|
---
|
||||||
|
|
||||||
|
# One-Click [Cloud Service]
|
||||||
|
|
||||||
|
:::note
|
||||||
|
This version of Immich is provided via cloud service providers' one-click marketplaces. Hosting costs are set by the cloud service providers.
|
||||||
|
Support for these are provided by the individual cloud service providers.
|
||||||
|
|
||||||
|
**Please report issues to the corresponding [Github Repository][github].**
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Go to the provider's marketplace and choose Immich, then follow the provided instructions.
|
||||||
|
|
||||||
|
## One-Click Immich marketplace providers
|
||||||
|
|
||||||
|
### DigitalOcean
|
||||||
|
|
||||||
|
https://marketplace.digitalocean.com/apps/immich
|
||||||
|
|
||||||
|
### Vultr
|
||||||
|
|
||||||
|
https://www.vultr.com/marketplace/apps/immich
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
|
||||||
|
For issues, open an issue on the associated [GitHub Repository][github].
|
||||||
|
|
||||||
|
[github]: https://github.com/immich-app/immich/
|
||||||
+1
-1
@@ -57,6 +57,6 @@
|
|||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.19.0"
|
"node": "22.20.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
22.19.0
|
22.20.0
|
||||||
|
|||||||
+1
-1
@@ -52,6 +52,6 @@
|
|||||||
"vitest": "^3.0.0"
|
"vitest": "^3.0.0"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.19.0"
|
"node": "22.20.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[tools]
|
[tools]
|
||||||
node = "22.19.0"
|
node = "22.20.0"
|
||||||
flutter = "3.35.4"
|
flutter = "3.35.4"
|
||||||
pnpm = "10.15.1"
|
pnpm = "10.15.1"
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -84,4 +84,4 @@ Below is how your code needs to be structured:
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Please refer to the [architecture](https://immich.app/docs/developer/architecture/) for contributing to the mobile app!
|
Please refer to the [architecture](https://docs.immich.app/developer/architecture/) for contributing to the mobile app!
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ sealed class BaseAsset {
|
|||||||
|
|
||||||
// Overridden in subclasses
|
// Overridden in subclasses
|
||||||
AssetState get storage;
|
AssetState get storage;
|
||||||
|
String? get localId;
|
||||||
|
String? get remoteId;
|
||||||
String get heroTag;
|
String get heroTag;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ part of 'base_asset.model.dart';
|
|||||||
|
|
||||||
class LocalAsset extends BaseAsset {
|
class LocalAsset extends BaseAsset {
|
||||||
final String id;
|
final String id;
|
||||||
final String? remoteId;
|
final String? remoteAssetId;
|
||||||
final int orientation;
|
final int orientation;
|
||||||
|
|
||||||
const LocalAsset({
|
const LocalAsset({
|
||||||
required this.id,
|
required this.id,
|
||||||
this.remoteId,
|
String? remoteId,
|
||||||
required super.name,
|
required super.name,
|
||||||
super.checksum,
|
super.checksum,
|
||||||
required super.type,
|
required super.type,
|
||||||
@@ -19,7 +19,13 @@ class LocalAsset extends BaseAsset {
|
|||||||
super.isFavorite = false,
|
super.isFavorite = false,
|
||||||
super.livePhotoVideoId,
|
super.livePhotoVideoId,
|
||||||
this.orientation = 0,
|
this.orientation = 0,
|
||||||
});
|
}) : remoteAssetId = remoteId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get localId => id;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get remoteId => remoteAssetId;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
AssetState get storage => remoteId == null ? AssetState.local : AssetState.merged;
|
AssetState get storage => remoteId == null ? AssetState.local : AssetState.merged;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ enum AssetVisibility { timeline, hidden, archive, locked }
|
|||||||
// Model for an asset stored in the server
|
// Model for an asset stored in the server
|
||||||
class RemoteAsset extends BaseAsset {
|
class RemoteAsset extends BaseAsset {
|
||||||
final String id;
|
final String id;
|
||||||
final String? localId;
|
final String? localAssetId;
|
||||||
final String? thumbHash;
|
final String? thumbHash;
|
||||||
final AssetVisibility visibility;
|
final AssetVisibility visibility;
|
||||||
final String ownerId;
|
final String ownerId;
|
||||||
@@ -13,7 +13,7 @@ class RemoteAsset extends BaseAsset {
|
|||||||
|
|
||||||
const RemoteAsset({
|
const RemoteAsset({
|
||||||
required this.id,
|
required this.id,
|
||||||
this.localId,
|
String? localId,
|
||||||
required super.name,
|
required super.name,
|
||||||
required this.ownerId,
|
required this.ownerId,
|
||||||
required super.checksum,
|
required super.checksum,
|
||||||
@@ -28,7 +28,13 @@ class RemoteAsset extends BaseAsset {
|
|||||||
this.visibility = AssetVisibility.timeline,
|
this.visibility = AssetVisibility.timeline,
|
||||||
super.livePhotoVideoId,
|
super.livePhotoVideoId,
|
||||||
this.stackId,
|
this.stackId,
|
||||||
});
|
}) : localAssetId = localId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get localId => localAssetId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String? get remoteId => id;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
AssetState get storage => localId == null ? AssetState.remote : AssetState.merged;
|
AssetState get storage => localId == null ? AssetState.remote : AssetState.merged;
|
||||||
|
|||||||
@@ -84,8 +84,8 @@ class AssetService {
|
|||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<(String, String)>> getPlaces() {
|
Future<List<(String, String)>> getPlaces(String userId) {
|
||||||
return _remoteAssetRepository.getPlaces();
|
return _remoteAssetRepository.getPlaces(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<(int local, int remote)> getAssetCounts() async {
|
Future<(int local, int remote)> getAssetCounts() async {
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ class TimelineFactory {
|
|||||||
|
|
||||||
TimelineService fromAssets(List<BaseAsset> assets) => TimelineService(_timelineRepository.fromAssets(assets));
|
TimelineService fromAssets(List<BaseAsset> assets) => TimelineService(_timelineRepository.fromAssets(assets));
|
||||||
|
|
||||||
TimelineService map(LatLngBounds bounds) => TimelineService(_timelineRepository.map(bounds, groupBy));
|
TimelineService map(String userId, LatLngBounds bounds) =>
|
||||||
|
TimelineService(_timelineRepository.map(userId, bounds, groupBy));
|
||||||
}
|
}
|
||||||
|
|
||||||
class TimelineService {
|
class TimelineService {
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||||||
.toDto(
|
.toDto(
|
||||||
assetCount: row.read(assetCount) ?? 0,
|
assetCount: row.read(assetCount) ?? 0,
|
||||||
ownerName: row.read(_db.userEntity.name)!,
|
ownerName: row.read(_db.userEntity.name)!,
|
||||||
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 2,
|
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.get();
|
.get();
|
||||||
@@ -107,7 +107,7 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||||||
.toDto(
|
.toDto(
|
||||||
assetCount: row.read(assetCount) ?? 0,
|
assetCount: row.read(assetCount) ?? 0,
|
||||||
ownerName: row.read(_db.userEntity.name)!,
|
ownerName: row.read(_db.userEntity.name)!,
|
||||||
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 2,
|
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
@@ -305,8 +305,9 @@ class DriftRemoteAlbumRepository extends DriftDatabaseRepository {
|
|||||||
.readTable(_db.remoteAlbumEntity)
|
.readTable(_db.remoteAlbumEntity)
|
||||||
.toDto(
|
.toDto(
|
||||||
ownerName: row.read(_db.userEntity.name)!,
|
ownerName: row.read(_db.userEntity.name)!,
|
||||||
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 2,
|
isShared: row.read(_db.remoteAlbumUserEntity.userId.count(distinct: true))! > 0,
|
||||||
);
|
);
|
||||||
|
|
||||||
return album;
|
return album;
|
||||||
}).watchSingleOrNull();
|
}).watchSingleOrNull();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,9 +81,11 @@ class RemoteAssetRepository extends DriftDatabaseRepository {
|
|||||||
.getSingleOrNull();
|
.getSingleOrNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<(String, String)>> getPlaces() {
|
Future<List<(String, String)>> getPlaces(String userId) {
|
||||||
final asset = Subquery(
|
final asset = Subquery(
|
||||||
_db.remoteAssetEntity.select()..orderBy([(row) => OrderingTerm.desc(row.createdAt)]),
|
_db.remoteAssetEntity.select()
|
||||||
|
..where((row) => row.ownerId.equals(userId))
|
||||||
|
..orderBy([(row) => OrderingTerm.desc(row.createdAt)]),
|
||||||
"asset",
|
"asset",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -431,12 +431,16 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get();
|
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineQuery map(LatLngBounds bounds, GroupAssetsBy groupBy) => (
|
TimelineQuery map(String userId, LatLngBounds bounds, GroupAssetsBy groupBy) => (
|
||||||
bucketSource: () => _watchMapBucket(bounds, groupBy: groupBy),
|
bucketSource: () => _watchMapBucket(userId, bounds, groupBy: groupBy),
|
||||||
assetSource: (offset, count) => _getMapBucketAssets(bounds, offset: offset, count: count),
|
assetSource: (offset, count) => _getMapBucketAssets(userId, bounds, offset: offset, count: count),
|
||||||
);
|
);
|
||||||
|
|
||||||
Stream<List<Bucket>> _watchMapBucket(LatLngBounds bounds, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
|
Stream<List<Bucket>> _watchMapBucket(
|
||||||
|
String userId,
|
||||||
|
LatLngBounds bounds, {
|
||||||
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
|
}) {
|
||||||
if (groupBy == GroupAssetsBy.none) {
|
if (groupBy == GroupAssetsBy.none) {
|
||||||
// TODO: Support GroupAssetsBy.none
|
// TODO: Support GroupAssetsBy.none
|
||||||
throw UnsupportedError("GroupAssetsBy.none is not supported for _watchMapBucket");
|
throw UnsupportedError("GroupAssetsBy.none is not supported for _watchMapBucket");
|
||||||
@@ -455,7 +459,8 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
),
|
),
|
||||||
])
|
])
|
||||||
..where(
|
..where(
|
||||||
_db.remoteExifEntity.inBounds(bounds) &
|
_db.remoteAssetEntity.ownerId.equals(userId) &
|
||||||
|
_db.remoteExifEntity.inBounds(bounds) &
|
||||||
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
|
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
|
||||||
_db.remoteAssetEntity.deletedAt.isNull(),
|
_db.remoteAssetEntity.deletedAt.isNull(),
|
||||||
)
|
)
|
||||||
@@ -469,7 +474,12 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
}).watch();
|
}).watch();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<List<BaseAsset>> _getMapBucketAssets(LatLngBounds bounds, {required int offset, required int count}) {
|
Future<List<BaseAsset>> _getMapBucketAssets(
|
||||||
|
String userId,
|
||||||
|
LatLngBounds bounds, {
|
||||||
|
required int offset,
|
||||||
|
required int count,
|
||||||
|
}) {
|
||||||
final query =
|
final query =
|
||||||
_db.remoteAssetEntity.select().join([
|
_db.remoteAssetEntity.select().join([
|
||||||
innerJoin(
|
innerJoin(
|
||||||
@@ -479,7 +489,8 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
),
|
),
|
||||||
])
|
])
|
||||||
..where(
|
..where(
|
||||||
_db.remoteExifEntity.inBounds(bounds) &
|
_db.remoteAssetEntity.ownerId.equals(userId) &
|
||||||
|
_db.remoteExifEntity.inBounds(bounds) &
|
||||||
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
|
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
|
||||||
_db.remoteAssetEntity.deletedAt.isNull(),
|
_db.remoteAssetEntity.deletedAt.isNull(),
|
||||||
)
|
)
|
||||||
|
|||||||
+12
-1
@@ -8,6 +8,7 @@ import 'package:immich_mobile/presentation/widgets/action_buttons/base_action_bu
|
|||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/action.provider.dart';
|
||||||
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
import 'package:immich_mobile/providers/timeline/multiselect.provider.dart';
|
||||||
|
import 'package:immich_mobile/widgets/asset_grid/delete_dialog.dart';
|
||||||
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
import 'package:immich_mobile/widgets/common/immich_toast.dart';
|
||||||
|
|
||||||
/// This delete action has the following behavior:
|
/// This delete action has the following behavior:
|
||||||
@@ -22,7 +23,17 @@ class DeleteLocalActionButton extends ConsumerWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final result = await ref.read(actionProvider.notifier).deleteLocal(source);
|
bool? backedUpOnly = await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) => DeleteLocalOnlyDialog(onDeleteLocal: (_) {}),
|
||||||
|
);
|
||||||
|
|
||||||
|
if (backedUpOnly == null) {
|
||||||
|
// User cancelled the dialog
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final result = await ref.read(actionProvider.notifier).deleteLocal(source, backedUpOnly);
|
||||||
ref.read(multiSelectProvider.notifier).reset();
|
ref.read(multiSelectProvider.notifier).reset();
|
||||||
|
|
||||||
if (source == ActionSource.viewer) {
|
if (source == ActionSource.viewer) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:immich_mobile/presentation/widgets/bottom_sheet/base_bottom_shee
|
|||||||
import 'package:immich_mobile/presentation/widgets/map/map.state.dart';
|
import 'package:immich_mobile/presentation/widgets/map/map.state.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
|
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
|
|
||||||
class MapBottomSheet extends StatelessWidget {
|
class MapBottomSheet extends StatelessWidget {
|
||||||
const MapBottomSheet({super.key});
|
const MapBottomSheet({super.key});
|
||||||
@@ -32,8 +33,13 @@ class _ScopedMapTimeline extends StatelessWidget {
|
|||||||
return ProviderScope(
|
return ProviderScope(
|
||||||
overrides: [
|
overrides: [
|
||||||
timelineServiceProvider.overrideWith((ref) {
|
timelineServiceProvider.overrideWith((ref) {
|
||||||
|
final user = ref.watch(currentUserProvider);
|
||||||
|
if (user == null) {
|
||||||
|
throw Exception('User must be logged in to access archive');
|
||||||
|
}
|
||||||
|
|
||||||
final bounds = ref.watch(mapStateProvider).bounds;
|
final bounds = ref.watch(mapStateProvider).bounds;
|
||||||
final timelineService = ref.watch(timelineFactoryProvider).map(bounds);
|
final timelineService = ref.watch(timelineFactoryProvider).map(user.id, bounds);
|
||||||
ref.onDispose(timelineService.dispose);
|
ref.onDispose(timelineService.dispose);
|
||||||
return timelineService;
|
return timelineService;
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -257,8 +257,15 @@ class ActionNotifier extends Notifier<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ActionResult> deleteLocal(ActionSource source) async {
|
Future<ActionResult> deleteLocal(ActionSource source, bool backedUpOnly) async {
|
||||||
final ids = _getLocalIdsForSource(source);
|
final List<String> ids;
|
||||||
|
if (backedUpOnly) {
|
||||||
|
final assets = _getAssets(source);
|
||||||
|
ids = assets.where((asset) => asset.storage == AssetState.merged).map((asset) => asset.localId!).toList();
|
||||||
|
} else {
|
||||||
|
ids = _getLocalIdsForSource(source);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final deletedCount = await _service.deleteLocal(ids);
|
final deletedCount = await _service.deleteLocal(ids);
|
||||||
return ActionResult(count: deletedCount, success: true);
|
return ActionResult(count: deletedCount, success: true);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:immich_mobile/domain/services/asset.service.dart';
|
|||||||
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart';
|
||||||
import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart';
|
import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart';
|
||||||
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
import 'package:immich_mobile/providers/infrastructure/db.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
|
|
||||||
final localAssetRepository = Provider<DriftLocalAssetRepository>(
|
final localAssetRepository = Provider<DriftLocalAssetRepository>(
|
||||||
(ref) => DriftLocalAssetRepository(ref.watch(driftProvider)),
|
(ref) => DriftLocalAssetRepository(ref.watch(driftProvider)),
|
||||||
@@ -19,9 +20,13 @@ final assetServiceProvider = Provider(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
final placesProvider = FutureProvider<List<(String, String)>>(
|
final placesProvider = FutureProvider<List<(String, String)>>((ref) {
|
||||||
(ref) => AssetService(
|
final assetService = ref.watch(assetServiceProvider);
|
||||||
remoteAssetRepository: ref.watch(remoteAssetRepositoryProvider),
|
final auth = ref.watch(currentUserProvider);
|
||||||
localAssetRepository: ref.watch(localAssetRepository),
|
|
||||||
).getPlaces(),
|
if (auth == null) {
|
||||||
);
|
return Future.value(const []);
|
||||||
|
}
|
||||||
|
|
||||||
|
return assetService.getPlaces(auth.id);
|
||||||
|
});
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
@@ -8,6 +9,7 @@ import 'package:immich_mobile/domain/models/store.model.dart';
|
|||||||
import 'package:immich_mobile/entities/asset.entity.dart' as asset_entity;
|
import 'package:immich_mobile/entities/asset.entity.dart' as asset_entity;
|
||||||
import 'package:immich_mobile/entities/store.entity.dart';
|
import 'package:immich_mobile/entities/store.entity.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/extensions/platform_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/response_extensions.dart';
|
import 'package:immich_mobile/extensions/response_extensions.dart';
|
||||||
import 'package:immich_mobile/repositories/asset_api.repository.dart';
|
import 'package:immich_mobile/repositories/asset_api.repository.dart';
|
||||||
import 'package:immich_mobile/utils/hash.dart';
|
import 'package:immich_mobile/utils/hash.dart';
|
||||||
@@ -20,11 +22,26 @@ final assetMediaRepositoryProvider = Provider((ref) => AssetMediaRepository(ref.
|
|||||||
|
|
||||||
class AssetMediaRepository {
|
class AssetMediaRepository {
|
||||||
final AssetApiRepository _assetApiRepository;
|
final AssetApiRepository _assetApiRepository;
|
||||||
|
|
||||||
static final Logger _log = Logger("AssetMediaRepository");
|
static final Logger _log = Logger("AssetMediaRepository");
|
||||||
|
|
||||||
const AssetMediaRepository(this._assetApiRepository);
|
const AssetMediaRepository(this._assetApiRepository);
|
||||||
|
|
||||||
Future<List<String>> deleteAll(List<String> ids) => PhotoManager.editor.deleteWithIds(ids);
|
Future<List<String>> deleteAll(List<String> ids) async {
|
||||||
|
if (CurrentPlatform.isIOS) {
|
||||||
|
return PhotoManager.editor.deleteWithIds(ids);
|
||||||
|
} else if (CurrentPlatform.isAndroid) {
|
||||||
|
final androidInfo = await DeviceInfoPlugin().androidInfo;
|
||||||
|
if (androidInfo.version.sdkInt < 30) {
|
||||||
|
return PhotoManager.editor.deleteWithIds(ids);
|
||||||
|
}
|
||||||
|
return PhotoManager.editor.android.moveToTrash(
|
||||||
|
// Only the id is needed
|
||||||
|
ids.map((id) => AssetEntity(id: id, width: 1, height: 1, typeInt: 0)).toList(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
Future<asset_entity.Asset?> get(String id) async {
|
Future<asset_entity.Asset?> get(String id) async {
|
||||||
final entity = await AssetEntity.fromId(id);
|
final entity = await AssetEntity.fromId(id);
|
||||||
@@ -72,6 +89,7 @@ class AssetMediaRepository {
|
|||||||
// TODO: make this more efficient
|
// TODO: make this more efficient
|
||||||
Future<int> shareAssets(List<BaseAsset> assets, BuildContext context) async {
|
Future<int> shareAssets(List<BaseAsset> assets, BuildContext context) async {
|
||||||
final downloadedXFiles = <XFile>[];
|
final downloadedXFiles = <XFile>[];
|
||||||
|
final tempFiles = <File>[];
|
||||||
|
|
||||||
for (var asset in assets) {
|
for (var asset in assets) {
|
||||||
final localId = (asset is LocalAsset)
|
final localId = (asset is LocalAsset)
|
||||||
@@ -82,6 +100,9 @@ class AssetMediaRepository {
|
|||||||
if (localId != null) {
|
if (localId != null) {
|
||||||
File? f = await AssetEntity(id: localId, width: 1, height: 1, typeInt: 0).originFile;
|
File? f = await AssetEntity(id: localId, width: 1, height: 1, typeInt: 0).originFile;
|
||||||
downloadedXFiles.add(XFile(f!.path));
|
downloadedXFiles.add(XFile(f!.path));
|
||||||
|
if (CurrentPlatform.isIOS) {
|
||||||
|
tempFiles.add(f);
|
||||||
|
}
|
||||||
} else if (asset is RemoteAsset) {
|
} else if (asset is RemoteAsset) {
|
||||||
final tempDir = await getTemporaryDirectory();
|
final tempDir = await getTemporaryDirectory();
|
||||||
final name = asset.name;
|
final name = asset.name;
|
||||||
@@ -95,6 +116,7 @@ class AssetMediaRepository {
|
|||||||
|
|
||||||
await tempFile.writeAsBytes(res.bodyBytes);
|
await tempFile.writeAsBytes(res.bodyBytes);
|
||||||
downloadedXFiles.add(XFile(tempFile.path));
|
downloadedXFiles.add(XFile(tempFile.path));
|
||||||
|
tempFiles.add(tempFile);
|
||||||
} else {
|
} else {
|
||||||
_log.warning("Asset type not supported for sharing: $asset");
|
_log.warning("Asset type not supported for sharing: $asset");
|
||||||
continue;
|
continue;
|
||||||
@@ -113,9 +135,9 @@ class AssetMediaRepository {
|
|||||||
downloadedXFiles,
|
downloadedXFiles,
|
||||||
sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)),
|
sharePositionOrigin: Rect.fromPoints(Offset.zero, Offset(size.width / 3, size.height)),
|
||||||
).then((result) async {
|
).then((result) async {
|
||||||
for (var file in downloadedXFiles) {
|
for (var file in tempFiles) {
|
||||||
try {
|
try {
|
||||||
await File(file.path).delete();
|
await file.delete();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_log.warning("Failed to delete temporary file: ${file.path}", e);
|
_log.warning("Failed to delete temporary file: ${file.path}", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,12 +22,12 @@ class DeleteLocalOnlyDialog extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
void onDeleteBackedUpOnly() {
|
void onDeleteBackedUpOnly() {
|
||||||
context.pop();
|
context.pop(true);
|
||||||
onDeleteLocal(true);
|
onDeleteLocal(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void onForceDelete() {
|
void onForceDelete() {
|
||||||
context.pop();
|
context.pop(false);
|
||||||
onDeleteLocal(false);
|
onDeleteLocal(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,26 +36,44 @@ class DeleteLocalOnlyDialog extends StatelessWidget {
|
|||||||
title: const Text("delete_dialog_title").tr(),
|
title: const Text("delete_dialog_title").tr(),
|
||||||
content: const Text("delete_dialog_alert_local_non_backed_up").tr(),
|
content: const Text("delete_dialog_alert_local_non_backed_up").tr(),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
SizedBox(
|
||||||
onPressed: () => context.pop(),
|
width: double.infinity,
|
||||||
child: Text(
|
height: 48,
|
||||||
"cancel",
|
child: FilledButton(
|
||||||
style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold),
|
onPressed: () => context.pop(),
|
||||||
).tr(),
|
style: FilledButton.styleFrom(
|
||||||
|
backgroundColor: context.colorScheme.surfaceDim,
|
||||||
|
foregroundColor: context.primaryColor,
|
||||||
|
),
|
||||||
|
child: const Text("cancel", style: TextStyle(fontWeight: FontWeight.bold)).tr(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
TextButton(
|
const SizedBox(height: 8),
|
||||||
onPressed: onDeleteBackedUpOnly,
|
SizedBox(
|
||||||
child: Text(
|
width: double.infinity,
|
||||||
"delete_local_dialog_ok_backed_up_only",
|
height: 48,
|
||||||
style: TextStyle(color: context.colorScheme.tertiary, fontWeight: FontWeight.bold),
|
|
||||||
).tr(),
|
child: FilledButton(
|
||||||
|
onPressed: onDeleteBackedUpOnly,
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
backgroundColor: context.colorScheme.errorContainer,
|
||||||
|
foregroundColor: context.colorScheme.onErrorContainer,
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
"delete_local_dialog_ok_backed_up_only",
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold),
|
||||||
|
).tr(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
TextButton(
|
const SizedBox(height: 8),
|
||||||
onPressed: onForceDelete,
|
SizedBox(
|
||||||
child: Text(
|
width: double.infinity,
|
||||||
"delete_local_dialog_ok_force",
|
height: 48,
|
||||||
style: TextStyle(color: Colors.red[400], fontWeight: FontWeight.bold),
|
child: FilledButton(
|
||||||
).tr(),
|
onPressed: onForceDelete,
|
||||||
|
style: FilledButton.styleFrom(backgroundColor: Colors.red[400], foregroundColor: Colors.white),
|
||||||
|
child: const Text("delete_local_dialog_ok_force", style: TextStyle(fontWeight: FontWeight.bold)).tr(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
import 'package:auto_route/auto_route.dart';
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
import 'package:immich_mobile/extensions/translate_extensions.dart';
|
||||||
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
import 'package:immich_mobile/providers/app_settings.provider.dart';
|
||||||
import 'package:immich_mobile/providers/auth.provider.dart';
|
import 'package:immich_mobile/providers/auth.provider.dart';
|
||||||
import 'package:immich_mobile/providers/server_info.provider.dart';
|
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||||
|
|
||||||
@@ -16,10 +14,9 @@ class BetaTimelineListTile extends ConsumerWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final betaTimelineValue = ref.watch(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.betaTimeline);
|
final betaTimelineValue = ref.watch(appSettingsServiceProvider).getSetting<bool>(AppSettingsEnum.betaTimeline);
|
||||||
final serverInfo = ref.watch(serverInfoProvider);
|
|
||||||
final auth = ref.watch(authProvider);
|
final auth = ref.watch(authProvider);
|
||||||
|
|
||||||
if (!auth.isAuthenticated || (serverInfo.serverVersion.minor < 136 && kReleaseMode)) {
|
if (!auth.isAuthenticated) {
|
||||||
return const SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
22.19.0
|
22.20.0
|
||||||
|
|||||||
@@ -28,6 +28,6 @@
|
|||||||
"directory": "open-api/typescript-sdk"
|
"directory": "open-api/typescript-sdk"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.19.0"
|
"node": "22.20.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,13 +46,13 @@
|
|||||||
|
|
||||||
## محتوى
|
## محتوى
|
||||||
|
|
||||||
- [الوثائق الرسمية](https://immich.app/docs)
|
- [الوثائق الرسمية](https://docs.immich.app)
|
||||||
- [خريطة الطريق](https://github.com/orgs/immich-app/projects/1)
|
- [خريطة الطريق](https://github.com/orgs/immich-app/projects/1)
|
||||||
- [تجريبي](#demo)
|
- [تجريبي](#demo)
|
||||||
- [سمات](#features)
|
- [سمات](#features)
|
||||||
- [مقدمة](https://immich.app/docs/overview/introduction)
|
- [مقدمة](https://docs.immich.app/overview/introduction)
|
||||||
- [تعليمات التحميل](https://immich.app/docs/install/requirements)
|
- [تعليمات التحميل](https://docs.immich.app/install/requirements)
|
||||||
- [قواعد المساهمة](https://immich.app/docs/overview/support-the-project)
|
- [قواعد المساهمة](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## توثيق
|
## توثيق
|
||||||
|
|
||||||
|
|||||||
@@ -44,13 +44,13 @@
|
|||||||
|
|
||||||
## Contingut
|
## Contingut
|
||||||
|
|
||||||
- [Documentació oficial](https://immich.app/docs)
|
- [Documentació oficial](https://docs.immich.app)
|
||||||
- [Mapa de ruta](https://github.com/orgs/immich-app/projects/1)
|
- [Mapa de ruta](https://github.com/orgs/immich-app/projects/1)
|
||||||
- [Demo](#demo)
|
- [Demo](#demo)
|
||||||
- [Funcionalitats](#funcionalitats)
|
- [Funcionalitats](#funcionalitats)
|
||||||
- [Introducció](https://immich.app/docs/overview/introduction)
|
- [Introducció](https://docs.immich.app/overview/introduction)
|
||||||
- [Instal·lació](https://immich.app/docs/install/requirements)
|
- [Instal·lació](https://docs.immich.app/install/requirements)
|
||||||
- [Directrius de contribució](https://immich.app/docs/overview/support-the-project)
|
- [Directrius de contribució](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## Documentació
|
## Documentació
|
||||||
|
|
||||||
|
|||||||
@@ -50,14 +50,14 @@
|
|||||||
|
|
||||||
## Inhalt
|
## Inhalt
|
||||||
|
|
||||||
- [Offizielle Dokumentation](https://immich.app/docs)
|
- [Offizielle Dokumentation](https://docs.immich.app)
|
||||||
- [Über Immich](https://immich.app/docs/overview/introduction)
|
- [Über Immich](https://docs.immich.app/overview/introduction)
|
||||||
- [Installation](https://immich.app/docs/install/requirements)
|
- [Installation](https://docs.immich.app/install/requirements)
|
||||||
- [Roadmap](https://github.com/orgs/immich-app/projects/1)
|
- [Roadmap](https://github.com/orgs/immich-app/projects/1)
|
||||||
- [Demo](#demo)
|
- [Demo](#demo)
|
||||||
- [Funktionen](#funktionen)
|
- [Funktionen](#funktionen)
|
||||||
- [Übersetzungen](https://immich.app/docs/developer/translations)
|
- [Übersetzungen](https://docs.immich.app/developer/translations)
|
||||||
- [Beitragsrichtlinien](https://immich.app/docs/overview/support-the-project)
|
- [Beitragsrichtlinien](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ Die Web-Demo kannst Du unter https://demo.immich.app finden. Für die Handy-App
|
|||||||
|
|
||||||
## Übersetzungen
|
## Übersetzungen
|
||||||
|
|
||||||
Mehr zum Thema Übersetzungen kannst du [hier](https://immich.app/docs/developer/translations) erfahren.
|
Mehr zum Thema Übersetzungen kannst du [hier](https://docs.immich.app/developer/translations) erfahren.
|
||||||
|
|
||||||
<a href="https://hosted.weblate.org/engage/immich/">
|
<a href="https://hosted.weblate.org/engage/immich/">
|
||||||
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Translation status" />
|
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Translation status" />
|
||||||
|
|||||||
@@ -45,13 +45,13 @@
|
|||||||
|
|
||||||
## Contenido
|
## Contenido
|
||||||
|
|
||||||
- [Documentación oficial](https://immich.app/docs)
|
- [Documentación oficial](https://docs.immich.app)
|
||||||
- [Hoja de ruta](https://github.com/orgs/immich-app/projects/1)
|
- [Hoja de ruta](https://github.com/orgs/immich-app/projects/1)
|
||||||
- [Demo](#demo)
|
- [Demo](#demo)
|
||||||
- [Funciones](#funciones)
|
- [Funciones](#funciones)
|
||||||
- [Introducción](https://immich.app/docs/overview/introduction)
|
- [Introducción](https://docs.immich.app/overview/introduction)
|
||||||
- [Instalación](https://immich.app/docs/install/requirements)
|
- [Instalación](https://docs.immich.app/install/requirements)
|
||||||
- [Directrices para contribuir](https://immich.app/docs/overview/support-the-project)
|
- [Directrices para contribuir](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## Documentación
|
## Documentación
|
||||||
|
|
||||||
@@ -99,7 +99,7 @@ contraseña: demo
|
|||||||
|
|
||||||
## Traducciones
|
## Traducciones
|
||||||
|
|
||||||
Lea mas acerca de las traducciones [acá](https://immich.app/docs/developer/translations).
|
Lea mas acerca de las traducciones [acá](https://docs.immich.app/developer/translations).
|
||||||
|
|
||||||
<a href="https://hosted.weblate.org/engage/immich/">
|
<a href="https://hosted.weblate.org/engage/immich/">
|
||||||
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Translation status" />
|
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Translation status" />
|
||||||
|
|||||||
@@ -45,13 +45,13 @@
|
|||||||
|
|
||||||
## Sommaire
|
## Sommaire
|
||||||
|
|
||||||
- [Documentation officielle](https://immich.app/docs)
|
- [Documentation officielle](https://docs.immich.app)
|
||||||
- [Feuille de route](https://github.com/orgs/immich-app/projects/1)
|
- [Feuille de route](https://github.com/orgs/immich-app/projects/1)
|
||||||
- [Démo](#démo)
|
- [Démo](#démo)
|
||||||
- [Fonctionnalités](#fonctionnalités)
|
- [Fonctionnalités](#fonctionnalités)
|
||||||
- [Introduction](https://immich.app/docs/overview/introduction)
|
- [Introduction](https://docs.immich.app/overview/introduction)
|
||||||
- [Installation](https://immich.app/docs/install/requirements)
|
- [Installation](https://docs.immich.app/install/requirements)
|
||||||
- [Contribution](https://immich.app/docs/overview/support-the-project)
|
- [Contribution](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
|
|||||||
@@ -49,14 +49,14 @@
|
|||||||
|
|
||||||
## Link utili
|
## Link utili
|
||||||
|
|
||||||
- [Documentazione](https://immich.app/docs)
|
- [Documentazione](https://docs.immich.app)
|
||||||
- [Informazioni](https://immich.app/docs/overview/introduction)
|
- [Informazioni](https://docs.immich.app/overview/introduction)
|
||||||
- [Installazione](https://immich.app/docs/install/requirements)
|
- [Installazione](https://docs.immich.app/install/requirements)
|
||||||
- [Roadmap](https://immich.app/roadmap)
|
- [Roadmap](https://immich.app/roadmap)
|
||||||
- [Demo](#demo)
|
- [Demo](#demo)
|
||||||
- [Funzionalità](#funzionalità)
|
- [Funzionalità](#funzionalità)
|
||||||
- [Traduzioni](https://immich.app/docs/developer/translations)
|
- [Traduzioni](https://docs.immich.app/developer/translations)
|
||||||
- [Contribuire](https://immich.app/docs/overview/support-the-project)
|
- [Contribuire](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ Per l’app mobile puoi usare `https://demo.immich.app` come `Server Endpoint UR
|
|||||||
|
|
||||||
## Traduzioni
|
## Traduzioni
|
||||||
|
|
||||||
Scopri di più sulle traduzioni [qui](https://immich.app/docs/developer/translations).
|
Scopri di più sulle traduzioni [qui](https://docs.immich.app/developer/translations).
|
||||||
|
|
||||||
<a href="https://hosted.weblate.org/engage/immich/">
|
<a href="https://hosted.weblate.org/engage/immich/">
|
||||||
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Stato traduzioni" />
|
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Stato traduzioni" />
|
||||||
|
|||||||
@@ -44,13 +44,13 @@
|
|||||||
|
|
||||||
## コンテンツ
|
## コンテンツ
|
||||||
|
|
||||||
- [公式ドキュメント](https://immich.app/docs)
|
- [公式ドキュメント](https://docs.immich.app)
|
||||||
- [ロードマップ](https://github.com/orgs/immich-app/projects/1)
|
- [ロードマップ](https://github.com/orgs/immich-app/projects/1)
|
||||||
- [デモ](#デモ)
|
- [デモ](#デモ)
|
||||||
- [機能](#機能)
|
- [機能](#機能)
|
||||||
- [紹介](https://immich.app/docs/overview/introduction)
|
- [紹介](https://docs.immich.app/overview/introduction)
|
||||||
- [インストール](https://immich.app/docs/install/requirements)
|
- [インストール](https://docs.immich.app/install/requirements)
|
||||||
- [コントリビューションガイド](https://immich.app/docs/overview/support-the-project)
|
- [コントリビューションガイド](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## ドキュメント
|
## ドキュメント
|
||||||
|
|
||||||
|
|||||||
@@ -50,14 +50,14 @@
|
|||||||
|
|
||||||
## 링크
|
## 링크
|
||||||
|
|
||||||
- [문서](https://immich.app/docs)
|
- [문서](https://docs.immich.app)
|
||||||
- [소개](https://immich.app/docs/overview/introduction)
|
- [소개](https://docs.immich.app/overview/introduction)
|
||||||
- [설치](https://immich.app/docs/install/requirements)
|
- [설치](https://docs.immich.app/install/requirements)
|
||||||
- [로드맵](https://immich.app/roadmap)
|
- [로드맵](https://immich.app/roadmap)
|
||||||
- [데모](#데모)
|
- [데모](#데모)
|
||||||
- [기능](#기능)
|
- [기능](#기능)
|
||||||
- [번역](https://immich.app/docs/developer/tranlations)
|
- [번역](https://docs.immich.app/developer/tranlations)
|
||||||
- [기여](https://immich.app/docs/overview/support-the-project)
|
- [기여](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## 데모
|
## 데모
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
|
|
||||||
## 번역
|
## 번역
|
||||||
|
|
||||||
번역에 대한 자세한 정보는 [이곳](https://immich.app/docs/developer/translations)에서 확인하세요.
|
번역에 대한 자세한 정보는 [이곳](https://docs.immich.app/developer/translations)에서 확인하세요.
|
||||||
|
|
||||||
<a href="https://hosted.weblate.org/engage/immich/">
|
<a href="https://hosted.weblate.org/engage/immich/">
|
||||||
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="번역 현황" />
|
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="번역 현황" />
|
||||||
|
|||||||
@@ -45,13 +45,13 @@
|
|||||||
|
|
||||||
## Inhoud
|
## Inhoud
|
||||||
|
|
||||||
- [Officiële documentatie](https://immich.app/docs)
|
- [Officiële documentatie](https://docs.immich.app)
|
||||||
- [Toekomstplannen](https://github.com/orgs/immich-app/projects/1)
|
- [Toekomstplannen](https://github.com/orgs/immich-app/projects/1)
|
||||||
- [Demo](#demo)
|
- [Demo](#demo)
|
||||||
- [Functies](#functies)
|
- [Functies](#functies)
|
||||||
- [Introductie](https://immich.app/docs/overview/introduction)
|
- [Introductie](https://docs.immich.app/overview/introduction)
|
||||||
- [Installatie](https://immich.app/docs/install/requirements)
|
- [Installatie](https://docs.immich.app/install/requirements)
|
||||||
- [Richtlijnen voor bijdragen](https://immich.app/docs/overview/support-the-project)
|
- [Richtlijnen voor bijdragen](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## Documentatie
|
## Documentatie
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ Je kunt de demo [hier](https://demo.immich.app/) bekijken. Voor de mobiele app k
|
|||||||
|
|
||||||
## Vertalingen
|
## Vertalingen
|
||||||
|
|
||||||
Je kunt [hier](https://immich.app/docs/developer/translations) meer over vertalingen lezen.
|
Je kunt [hier](https://docs.immich.app/developer/translations) meer over vertalingen lezen.
|
||||||
|
|
||||||
## Repository activiteit
|
## Repository activiteit
|
||||||
|
|
||||||
|
|||||||
@@ -55,14 +55,14 @@
|
|||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
- [Documentação](https://immich.app/docs)
|
- [Documentação](https://docs.immich.app)
|
||||||
- [Sobre](https://immich.app/docs/overview/introduction)
|
- [Sobre](https://docs.immich.app/overview/introduction)
|
||||||
- [Instalação](https://immich.app/docs/install/requirements)
|
- [Instalação](https://docs.immich.app/install/requirements)
|
||||||
- [Roadmap](https://github.com/orgs/immich-app/projects/1)
|
- [Roadmap](https://github.com/orgs/immich-app/projects/1)
|
||||||
- [Demonstração](#demonstração)
|
- [Demonstração](#demonstração)
|
||||||
- [Funcionalidades](#funcionalidades)
|
- [Funcionalidades](#funcionalidades)
|
||||||
- [Traduções](https://immich.app/docs/developer/translations)
|
- [Traduções](https://docs.immich.app/developer/translations)
|
||||||
- [Diretrizes de Contribuição](https://immich.app/docs/overview/support-the-project)
|
- [Diretrizes de Contribuição](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## Demonstração
|
## Demonstração
|
||||||
|
|
||||||
@@ -115,7 +115,7 @@ Acesse a demonstração [aqui](https://demo.immich.app). No aplicativo para disp
|
|||||||
## Traduções
|
## Traduções
|
||||||
|
|
||||||
Leia mais sobre as traduções
|
Leia mais sobre as traduções
|
||||||
[aqui](https://immich.app/docs/developer/translations).
|
[aqui](https://docs.immich.app/developer/translations).
|
||||||
|
|
||||||
<a href="https://hosted.weblate.org/engage/immich/">
|
<a href="https://hosted.weblate.org/engage/immich/">
|
||||||
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Status da tradução" />
|
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Status da tradução" />
|
||||||
|
|||||||
@@ -51,14 +51,14 @@
|
|||||||
|
|
||||||
## Содержание
|
## Содержание
|
||||||
|
|
||||||
- [Официальная документация](https://immich.app/docs)
|
- [Официальная документация](https://docs.immich.app)
|
||||||
- [Введение](https://immich.app/docs/overview/introduction)
|
- [Введение](https://docs.immich.app/overview/introduction)
|
||||||
- [Установка](https://immich.app/docs/install/requirements)
|
- [Установка](https://docs.immich.app/install/requirements)
|
||||||
- [План разработки](https://github.com/orgs/immich-app/projects/1)
|
- [План разработки](https://github.com/orgs/immich-app/projects/1)
|
||||||
- [Демо](#demo)
|
- [Демо](#demo)
|
||||||
- [Возможности](#features)
|
- [Возможности](#features)
|
||||||
- [Перевод](https://immich.app/docs/developer/translations)
|
- [Перевод](https://docs.immich.app/developer/translations)
|
||||||
- [Гид по участию и поддержке проекта](https://immich.app/docs/overview/support-the-project)
|
- [Гид по участию и поддержке проекта](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## Демо
|
## Демо
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
|
|
||||||
## Перевод
|
## Перевод
|
||||||
|
|
||||||
Всё про перевод проекта [Здесь](https://immich.app/docs/developer/translations).
|
Всё про перевод проекта [Здесь](https://docs.immich.app/developer/translations).
|
||||||
|
|
||||||
<a href="https://hosted.weblate.org/engage/immich/">
|
<a href="https://hosted.weblate.org/engage/immich/">
|
||||||
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Translation status" />
|
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Translation status" />
|
||||||
|
|||||||
@@ -46,13 +46,13 @@
|
|||||||
|
|
||||||
## Innehåll
|
## Innehåll
|
||||||
|
|
||||||
- [Officiell Dokumentation](https://immich.app/docs)
|
- [Officiell Dokumentation](https://docs.immich.app)
|
||||||
- [Roadmap](https://github.com/orgs/immich-app/projects/1)
|
- [Roadmap](https://github.com/orgs/immich-app/projects/1)
|
||||||
- [Demo](#demo)
|
- [Demo](#demo)
|
||||||
- [Funktioner](#features)
|
- [Funktioner](#features)
|
||||||
- [Introduktion](https://immich.app/docs/overview/introduction)
|
- [Introduktion](https://docs.immich.app/overview/introduction)
|
||||||
- [Installation](https://immich.app/docs/install/requirements)
|
- [Installation](https://docs.immich.app/install/requirements)
|
||||||
- [Riktlinjer för Bidrag](https://immich.app/docs/overview/support-the-project)
|
- [Riktlinjer för Bidrag](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## Dokumentation
|
## Dokumentation
|
||||||
|
|
||||||
|
|||||||
@@ -52,14 +52,14 @@
|
|||||||
|
|
||||||
## ลิงก์
|
## ลิงก์
|
||||||
|
|
||||||
- [คู่มือ](https://immich.app/docs)
|
- [คู่มือ](https://docs.immich.app)
|
||||||
- [เกี่ยวกับ](https://immich.app/docs/overview/introduction)
|
- [เกี่ยวกับ](https://docs.immich.app/overview/introduction)
|
||||||
- [การติดตั้ง](https://immich.app/docs/install/requirements)
|
- [การติดตั้ง](https://docs.immich.app/install/requirements)
|
||||||
- [โรดแมป](https://immich.app/roadmap)
|
- [โรดแมป](https://immich.app/roadmap)
|
||||||
- [สาธิต](#สาธิต)
|
- [สาธิต](#สาธิต)
|
||||||
- [คุณสมบัติ](#คุณสมบัติ)
|
- [คุณสมบัติ](#คุณสมบัติ)
|
||||||
- [การแปลภาษา](https://immich.app/docs/developer/translations)
|
- [การแปลภาษา](https://docs.immich.app/developer/translations)
|
||||||
- [สนับสนุนโพรเจกต์](https://immich.app/docs/overview/support-the-project)
|
- [สนับสนุนโพรเจกต์](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## สาธิต
|
## สาธิต
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@
|
|||||||
|
|
||||||
## การแปลภาษา
|
## การแปลภาษา
|
||||||
|
|
||||||
อ่านเพิ่มเติมเกี่ยวกับการแปล [ที่นี่](https://immich.app/docs/developer/translations)
|
อ่านเพิ่มเติมเกี่ยวกับการแปล [ที่นี่](https://docs.immich.app/developer/translations)
|
||||||
|
|
||||||
<a href="https://hosted.weblate.org/engage/immich/">
|
<a href="https://hosted.weblate.org/engage/immich/">
|
||||||
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="สถานะการแปล" />
|
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="สถานะการแปล" />
|
||||||
|
|||||||
@@ -44,13 +44,13 @@
|
|||||||
|
|
||||||
## Content
|
## Content
|
||||||
|
|
||||||
- [Resmi Belgeler](https://immich.app/docs)
|
- [Resmi Belgeler](https://docs.immich.app)
|
||||||
- [Yol Haritası](https://github.com/orgs/immich-app/projects/1)
|
- [Yol Haritası](https://github.com/orgs/immich-app/projects/1)
|
||||||
- [Demo](#demo)
|
- [Demo](#demo)
|
||||||
- [Özellikler](#özellikler)
|
- [Özellikler](#özellikler)
|
||||||
- [Giriş](https://immich.app/docs/overview/introduction)
|
- [Giriş](https://docs.immich.app/overview/introduction)
|
||||||
- [Kurulum](https://immich.app/docs/install/requirements)
|
- [Kurulum](https://docs.immich.app/install/requirements)
|
||||||
- [Katkı Sağlama Rehberi](https://immich.app/docs/overview/support-the-project)
|
- [Katkı Sağlama Rehberi](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## Belgeler
|
## Belgeler
|
||||||
|
|
||||||
|
|||||||
@@ -50,14 +50,14 @@
|
|||||||
|
|
||||||
## Посилання
|
## Посилання
|
||||||
|
|
||||||
- [Документація](https://immich.app/docs)
|
- [Документація](https://docs.immich.app)
|
||||||
- [Про проєкт](https://immich.app/docs/overview/introduction)
|
- [Про проєкт](https://docs.immich.app/overview/introduction)
|
||||||
- [Встановлення](https://immich.app/docs/install/requirements)
|
- [Встановлення](https://docs.immich.app/install/requirements)
|
||||||
- [Дорожня карта](https://immich.app/roadmap)
|
- [Дорожня карта](https://immich.app/roadmap)
|
||||||
- [Демо](#демо)
|
- [Демо](#демо)
|
||||||
- [Функції](#функції)
|
- [Функції](#функції)
|
||||||
- [Переклади](https://immich.app/docs/developer/translations)
|
- [Переклади](https://docs.immich.app/developer/translations)
|
||||||
- [Гід для розробки проєкту](https://immich.app/docs/overview/support-the-project)
|
- [Гід для розробки проєкту](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## Демо
|
## Демо
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@
|
|||||||
|
|
||||||
## Переклади
|
## Переклади
|
||||||
|
|
||||||
Більше про переклади [тут](https://immich.app/docs/developer/translations).
|
Більше про переклади [тут](https://docs.immich.app/developer/translations).
|
||||||
|
|
||||||
<a href="https://hosted.weblate.org/engage/immich/">
|
<a href="https://hosted.weblate.org/engage/immich/">
|
||||||
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Статус перекладів" />
|
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Статус перекладів" />
|
||||||
|
|||||||
@@ -52,14 +52,14 @@
|
|||||||
|
|
||||||
## Liên kết
|
## Liên kết
|
||||||
|
|
||||||
- [Tài liệu](https://immich.app/docs)
|
- [Tài liệu](https://docs.immich.app)
|
||||||
- [Giới thiệu](https://immich.app/docs/overview/introduction)
|
- [Giới thiệu](https://docs.immich.app/overview/introduction)
|
||||||
- [Cài đặt](https://immich.app/docs/install/requirements)
|
- [Cài đặt](https://docs.immich.app/install/requirements)
|
||||||
- [Lộ trình](https://immich.app/roadmap)
|
- [Lộ trình](https://immich.app/roadmap)
|
||||||
- [Demo](#demo)
|
- [Demo](#demo)
|
||||||
- [Tính năng](#Tính-năng)
|
- [Tính năng](#Tính-năng)
|
||||||
- [Dịch thuật](https://immich.app/docs/developer/translations)
|
- [Dịch thuật](https://docs.immich.app/developer/translations)
|
||||||
- [Đóng góp](https://immich.app/docs/overview/support-the-project)
|
- [Đóng góp](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## Demo
|
## Demo
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ Truy cập bản demo [tại đây](https://demo.immich.app). Đối với ứng
|
|||||||
|
|
||||||
## Dịch thuật
|
## Dịch thuật
|
||||||
|
|
||||||
Đọc thêm về dịch thuật [tại đây](https://immich.app/docs/developer/translations).
|
Đọc thêm về dịch thuật [tại đây](https://docs.immich.app/developer/translations).
|
||||||
|
|
||||||
<a href="https://hosted.weblate.org/engage/immich/">
|
<a href="https://hosted.weblate.org/engage/immich/">
|
||||||
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Tình trạng dịch thuật" />
|
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="Tình trạng dịch thuật" />
|
||||||
|
|||||||
@@ -54,14 +54,14 @@
|
|||||||
|
|
||||||
## 目录
|
## 目录
|
||||||
|
|
||||||
- [官方文档](https://immich.app/docs)
|
- [官方文档](https://docs.immich.app)
|
||||||
- [项目总览](https://immich.app/docs/overview/introduction)
|
- [项目总览](https://docs.immich.app/overview/introduction)
|
||||||
- [安装教程](https://immich.app/docs/install/requirements)
|
- [安装教程](https://docs.immich.app/install/requirements)
|
||||||
- [路线图](https://immich.app/roadmap)
|
- [路线图](https://immich.app/roadmap)
|
||||||
- [在线演示](#示例)
|
- [在线演示](#示例)
|
||||||
- [功能特性](#功能特性)
|
- [功能特性](#功能特性)
|
||||||
- [多语言](https://immich.app/docs/developer/translations)
|
- [多语言](https://docs.immich.app/developer/translations)
|
||||||
- [贡献者](https://immich.app/docs/overview/support-the-project)
|
- [贡献者](https://docs.immich.app/overview/support-the-project)
|
||||||
|
|
||||||
## 示例
|
## 示例
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
|
|
||||||
## 多语言
|
## 多语言
|
||||||
|
|
||||||
关于翻译的更多信息请参见[此处](https://immich.app/docs/developer/translations)。
|
关于翻译的更多信息请参见[此处](https://docs.immich.app/developer/translations)。
|
||||||
|
|
||||||
<a href="https://hosted.weblate.org/engage/immich/">
|
<a href="https://hosted.weblate.org/engage/immich/">
|
||||||
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="翻译进度" />
|
<img src="https://hosted.weblate.org/widget/immich/immich/multi-auto.svg" alt="翻译进度" />
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
22.19.0
|
22.20.0
|
||||||
|
|||||||
+1
-1
@@ -161,7 +161,7 @@
|
|||||||
"vitest": "^3.0.0"
|
"vitest": "^3.0.0"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.19.0"
|
"node": "22.20.0"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"sharp": "^0.34.3"
|
"sharp": "^0.34.3"
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const messages = {
|
|||||||
The ${name} extension version is ${version}, which means it is a nightly release.
|
The ${name} extension version is ${version}, which means it is a nightly release.
|
||||||
|
|
||||||
Please run 'DROP EXTENSION IF EXISTS ${extension}' and switch to a release version.
|
Please run 'DROP EXTENSION IF EXISTS ${extension}' and switch to a release version.
|
||||||
See https://immich.app/docs/guides/database-queries for how to query the database.`,
|
See https://docs.immich.app/guides/database-queries for how to query the database.`,
|
||||||
outOfRange: ({ name, version, range }: OutOfRangeArgs) =>
|
outOfRange: ({ name, version, range }: OutOfRangeArgs) =>
|
||||||
`The ${name} extension version is ${version}, but Immich only supports ${range}.
|
`The ${name} extension version is ${version}, but Immich only supports ${range}.
|
||||||
Please change ${name} to a compatible version in the Postgres instance.`,
|
Please change ${name} to a compatible version in the Postgres instance.`,
|
||||||
@@ -32,20 +32,20 @@ const messages = {
|
|||||||
|
|
||||||
If the Postgres instance already has ${name} installed, Immich may not have the necessary permissions to activate it.
|
If the Postgres instance already has ${name} installed, Immich may not have the necessary permissions to activate it.
|
||||||
In this case, please run 'CREATE EXTENSION IF NOT EXISTS ${extension} CASCADE' manually as a superuser.
|
In this case, please run 'CREATE EXTENSION IF NOT EXISTS ${extension} CASCADE' manually as a superuser.
|
||||||
See https://immich.app/docs/guides/database-queries for how to query the database.`,
|
See https://docs.immich.app/guides/database-queries for how to query the database.`,
|
||||||
updateFailed: ({ name, extension, availableVersion }: UpdateFailedArgs) =>
|
updateFailed: ({ name, extension, availableVersion }: UpdateFailedArgs) =>
|
||||||
`The ${name} extension can be updated to ${availableVersion}.
|
`The ${name} extension can be updated to ${availableVersion}.
|
||||||
Immich attempted to update the extension, but failed to do so.
|
Immich attempted to update the extension, but failed to do so.
|
||||||
This may be because Immich does not have the necessary permissions to update the extension.
|
This may be because Immich does not have the necessary permissions to update the extension.
|
||||||
|
|
||||||
Please run 'ALTER EXTENSION ${extension} UPDATE' manually as a superuser.
|
Please run 'ALTER EXTENSION ${extension} UPDATE' manually as a superuser.
|
||||||
See https://immich.app/docs/guides/database-queries for how to query the database.`,
|
See https://docs.immich.app/guides/database-queries for how to query the database.`,
|
||||||
dropFailed: ({ name, extension }: DropFailedArgs) =>
|
dropFailed: ({ name, extension }: DropFailedArgs) =>
|
||||||
`The ${name} extension is no longer needed, but could not be dropped.
|
`The ${name} extension is no longer needed, but could not be dropped.
|
||||||
This may be because Immich does not have the necessary permissions to drop the extension.
|
This may be because Immich does not have the necessary permissions to drop the extension.
|
||||||
|
|
||||||
Please run 'DROP EXTENSION ${extension};' manually as a superuser.
|
Please run 'DROP EXTENSION ${extension};' manually as a superuser.
|
||||||
See https://immich.app/docs/guides/database-queries for how to query the database.`,
|
See https://docs.immich.app/guides/database-queries for how to query the database.`,
|
||||||
restartRequired: ({ name, availableVersion }: RestartRequiredArgs) =>
|
restartRequired: ({ name, availableVersion }: RestartRequiredArgs) =>
|
||||||
`The ${name} extension has been updated to ${availableVersion}.
|
`The ${name} extension has been updated to ${availableVersion}.
|
||||||
Please restart the Postgres instance to complete the update.`,
|
Please restart the Postgres instance to complete the update.`,
|
||||||
@@ -55,7 +55,7 @@ const messages = {
|
|||||||
If ${name} ${installedVersion} is compatible with Immich, please ensure the Postgres instance has this available.`,
|
If ${name} ${installedVersion} is compatible with Immich, please ensure the Postgres instance has this available.`,
|
||||||
deprecatedExtension: (name: string) =>
|
deprecatedExtension: (name: string) =>
|
||||||
`DEPRECATION WARNING: The ${name} extension is deprecated and support for it will be removed very soon.
|
`DEPRECATION WARNING: The ${name} extension is deprecated and support for it will be removed very soon.
|
||||||
See https://immich.app/docs/install/upgrading#migrating-to-vectorchord in order to switch to the VectorChord extension instead.`,
|
See https://docs.immich.app/install/upgrading#migrating-to-vectorchord in order to switch to the VectorChord extension instead.`,
|
||||||
};
|
};
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import { BaseService } from 'src/services/base.service';
|
|||||||
import { JobOf, SystemFlags } from 'src/types';
|
import { JobOf, SystemFlags } from 'src/types';
|
||||||
import { ImmichStartupError } from 'src/utils/misc';
|
import { ImmichStartupError } from 'src/utils/misc';
|
||||||
|
|
||||||
const docsMessage = `Please see https://immich.app/docs/administration/system-integrity#folder-checks for more information.`;
|
const docsMessage = `Please see https://docs.immich.app/administration/system-integrity#folder-checks for more information.`;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class StorageService extends BaseService {
|
export class StorageService extends BaseService {
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
22.19.0
|
22.20.0
|
||||||
|
|||||||
+1
-1
@@ -107,6 +107,6 @@
|
|||||||
"vitest": "^3.0.0"
|
"vitest": "^3.0.0"
|
||||||
},
|
},
|
||||||
"volta": {
|
"volta": {
|
||||||
"node": "22.19.0"
|
"node": "22.20.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,7 +86,7 @@
|
|||||||
<FormatMessage key="admin.oauth_settings_more_details">
|
<FormatMessage key="admin.oauth_settings_more_details">
|
||||||
{#snippet children({ message })}
|
{#snippet children({ message })}
|
||||||
<a
|
<a
|
||||||
href="https://immich.app/docs/administration/oauth"
|
href="https://docs.immich.app/administration/oauth"
|
||||||
class="underline"
|
class="underline"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
|
|||||||
@@ -67,7 +67,7 @@
|
|||||||
<FormatMessage key="admin.map_manage_reverse_geocoding_settings">
|
<FormatMessage key="admin.map_manage_reverse_geocoding_settings">
|
||||||
{#snippet children({ message })}
|
{#snippet children({ message })}
|
||||||
<a
|
<a
|
||||||
href="https://immich.app/docs/features/reverse-geocoding"
|
href="https://docs.immich.app/features/reverse-geocoding"
|
||||||
class="underline"
|
class="underline"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
|
|||||||
@@ -119,7 +119,7 @@
|
|||||||
{#snippet children({ tag, message })}
|
{#snippet children({ tag, message })}
|
||||||
{#if tag === 'template-link'}
|
{#if tag === 'template-link'}
|
||||||
<a
|
<a
|
||||||
href="https://immich.app/docs/administration/storage-template"
|
href="https://docs.immich.app/administration/storage-template"
|
||||||
class="underline"
|
class="underline"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
</a>
|
</a>
|
||||||
{:else if tag === 'implications-link'}
|
{:else if tag === 'implications-link'}
|
||||||
<a
|
<a
|
||||||
href="https://immich.app/docs/administration/backup-and-restore#asset-types-and-storage-locations"
|
href="https://docs.immich.app/administration/backup-and-restore#asset-types-and-storage-locations"
|
||||||
class="underline"
|
class="underline"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
|
|||||||
@@ -91,7 +91,7 @@
|
|||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="https://immich.app/docs/guides/docker-help"
|
href="https://docs.immich.app/guides/docker-help"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="flex grow basis-0 justify-center p-4"
|
class="flex grow basis-0 justify-center p-4"
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
<FormatMessage key="admin.backup_onboarding_footer">
|
<FormatMessage key="admin.backup_onboarding_footer">
|
||||||
{#snippet children({ message })}
|
{#snippet children({ message })}
|
||||||
<a
|
<a
|
||||||
href="https://immich.app/docs/administration/backup-and-restore/"
|
href="https://docs.immich.app/administration/backup-and-restore/"
|
||||||
class="underline"
|
class="underline"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<FormatMessage key="admin.storage_template_onboarding_description_v2">
|
<FormatMessage key="admin.storage_template_onboarding_description_v2">
|
||||||
{#snippet children({ message })}
|
{#snippet children({ message })}
|
||||||
<a class="underline" href="https://immich.app/docs/administration/storage-template">{message}</a>
|
<a class="underline" href="https://docs.immich.app/administration/storage-template">{message}</a>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</FormatMessage>
|
</FormatMessage>
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<FormatMessage key="admin.authentication_settings_reenable">
|
<FormatMessage key="admin.authentication_settings_reenable">
|
||||||
{#snippet children({ message })}
|
{#snippet children({ message })}
|
||||||
<a
|
<a
|
||||||
href="https://immich.app/docs/administration/server-commands"
|
href="https://docs.immich.app/administration/server-commands"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
class="underline"
|
class="underline"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@
|
|||||||
<p>{$t('official_immich_resources')}</p>
|
<p>{$t('official_immich_resources')}</p>
|
||||||
<div class="flex flex-col sm:grid sm:grid-cols-2 gap-2 mt-5">
|
<div class="flex flex-col sm:grid sm:grid-cols-2 gap-2 mt-5">
|
||||||
<div>
|
<div>
|
||||||
<a href="https://{info.version}.archive.immich.app/docs/overview/introduction" target="_blank" rel="noreferrer">
|
<a href="https://{info.version}.archive.docs.immich.app/overview/introduction" target="_blank" rel="noreferrer">
|
||||||
<Icon icon={mdiInformationOutline} size="1.5em" class="inline-block" />
|
<Icon icon={mdiInformationOutline} size="1.5em" class="inline-block" />
|
||||||
<p class="font-medium text-primary text-sm underline inline-block" id="documentation-label">
|
<p class="font-medium text-primary text-sm underline inline-block" id="documentation-label">
|
||||||
{$t('documentation')}
|
{$t('documentation')}
|
||||||
|
|||||||
Reference in New Issue
Block a user