forked from Cutlery/immich
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2123e5c008 |
@@ -1,13 +1,9 @@
|
||||
# Jobs
|
||||
|
||||
The `immich-server` responds to API requests for data and files for the web and mobile app. To do this quickly and reliably, it offloads most other work to `immich-microservices` in the form of _jobs_. Simply put, a job is a request to process data in the background. Jobs are picked up automatically by microservices containers.
|
||||
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.
|
||||
|
||||
When a new asset is uploaded it kicks off a series of jobs, which include metadata extraction, thumbnail generation, machine learning tasks, and storage template migration, if enabled. To view the status of a job navigate to the Administration -> Jobs page.
|
||||
|
||||
Additionally, some jobs run on a schedule, which is every night at midnight. This schedule, with the exception of [External Libraries](/docs/features/libraries) scanning, cannot be changed.
|
||||

|
||||
|
||||
:::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.
|
||||
:::
|
||||
|
||||
<img src={require('./img/admin-jobs.png').default} width="80%" title="Admin jobs" />
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# Password Login
|
||||
|
||||
An overview of password login and related settings for Immich.
|
||||
|
||||
## Enable/Disable
|
||||
|
||||
Immich supports password login, which is enabled by default. The preferred way to disable it is via the [Administration Page](#administration-page), although it can also be changed via a [Server Command](#server-command) as well.
|
||||
|
||||
### Administration Page
|
||||
|
||||
To toggle the password login setting via the web, navigate to the "Administration", expand "Password Authentication", toggle the "Enabled" switch, and press "Save".
|
||||
|
||||

|
||||
|
||||
### Server Command
|
||||
|
||||
There are two [Server Commands](/docs/administration/server-commands.md) for password login:
|
||||
|
||||
1. `enable-password-login`
|
||||
2. `disable-password-login`
|
||||
|
||||
See [Server Commands](/docs/administration/server-commands.md) for more details about how to run them.
|
||||
|
||||
## Password Reset
|
||||
|
||||
### Admin
|
||||
|
||||
To reset the administrator password, use the `reset-admin-password` [Server Command](/docs/administration/server-commands.md).
|
||||
|
||||
### User
|
||||
|
||||
Immich does not currently support self-service password reset. However, the administration can reset passwords for other users. See [User Management: Password Reset](/docs/administration/user-management.mdx#password-reset) for more information about how to do this.
|
||||
@@ -1,173 +0,0 @@
|
||||
# System Settings
|
||||
|
||||
On the system settings page, the administrator can manage global settings for the Immich instance.
|
||||
|
||||
:::note
|
||||
Viewing and modifying the system settings is restricted to the Administrator.
|
||||
:::
|
||||
|
||||
:::tip
|
||||
You can always return to the default settings by clicking the `Reset to default` button.
|
||||
:::
|
||||
|
||||
## Job Settings
|
||||
|
||||
Using these settings, you can determine the amount of work that will run concurrently for each task in microservices. Some tasks can be set to higher values on computers with powerful hardware and storage with good I/O capabilities.
|
||||
|
||||
With higher concurrency, the host will work on more assets in parallel,
|
||||
this advice improves throughput, not latency, for example, it will make Smart Search jobs process more quickly, but it won't make searching faster.
|
||||
|
||||
It is important to remember that jobs like Smart Search, Face Detection, Facial Recognition, and Transcode Videos require a **lot** of processing power and therefore do not exaggerate the amount of jobs because you're probably thoroughly overloading the server.
|
||||
|
||||
:::info Facial Recognition Concurrency
|
||||
The Facial Recognition Concurrency value cannot be changed because
|
||||
[DBSCAN](https://www.youtube.com/watch?v=RDZUdRSDOok) is traditionally sequential, but there are parallel implementations of it out there. Our implementation isn't parallel.
|
||||
:::
|
||||
|
||||
## External Library
|
||||
|
||||
### Library watching (EXPERIMENTAL)
|
||||
|
||||
External libraries can automatically import changed files without a full rescan. It will import the file whenever the operating system reports a file change. If your photos are mounted over the network, this does not work.
|
||||
|
||||
### Periodic Scanning
|
||||
|
||||
You can define a custom interval for the trigger external library rescan under Administration -> Settings -> Library.
|
||||
You can set the scanning interval using the preset or cron format. For more information please refer to e.g. [Crontab Guru](https://crontab.guru/).
|
||||
|
||||
## Logging
|
||||
|
||||
By default logs are set to record at the log level, the network administrator can choose a deeper or lower level of logs according to his decision or according to the needs required by the Immich support team.
|
||||
|
||||
Here you can [learn about the different error levels](https://sematext.com/blog/logging-levels/).
|
||||
|
||||
## Machine Learning Settings
|
||||
|
||||
Through this setting, you can manage all the settings related to machine learning in Immich, from the setting of remote machine learning to the model and its parameters
|
||||
You can choose to disable a certain type of machine learning, for example smart search or facial recognition.
|
||||
|
||||
### Smart Search
|
||||
|
||||
The smart search settings are designed to allow the search tool to be used using [CLIP](https://openai.com/research/clip) models that [can be changed](/docs/FAQ#can-i-use-a-custom-clip-model), different models will necessarily give better results but may consume more processing power, when changing a model it is mandatory to re-run the
|
||||
Smart Search job on all images to fully apply the change.
|
||||
|
||||
:::info Internet connection
|
||||
Changing models requires a connection to the Internet to download the model.
|
||||
After downloading, there is no need for Immich to connect to the network
|
||||
Unless version checking has been enabled in the settings.
|
||||
:::
|
||||
|
||||
### Facial Recognition
|
||||
|
||||
Under these settings, you can change the facial recognition settings
|
||||
Editable settings:
|
||||
|
||||
- **Facial Recognition Model -** Models are listed in descending order of size. Larger models are slower and use more memory, but produce better results. Note that you must re-run the Face Detection job for all images upon changing a model.
|
||||
- **Min Detection Score -** Minimum confidence score for a face to be detected from 0-1. Lower values will detect more faces but may result in false positives.
|
||||
- **Max Recognition Distance -** Maximum distance between two faces to be considered the same person, ranging from 0-2. Lowering this can prevent labeling two people as the same person, while raising it can prevent labeling the same person as two different people. Note that it is easier to merge two people than to split one person in two, so err on the side of a lower threshold when possible.
|
||||
- **Min Recognized Faces -** The minimum number of recognized faces for a person to be created (AKA: Core face). Increasing this makes Facial Recognition more precise at the cost of increasing the chance that a face is not assigned to a person.
|
||||
|
||||
:::info
|
||||
When changing the values in Min Detection Score, Max Recognition Distance, and Min Recognized Faces.
|
||||
You will have to restart **only** the job FACIAL RECOGNITION - ALL.
|
||||
|
||||
If you replace the Facial Recognition Model, you will have to run the job FACE DETECTION - ALL.
|
||||
:::
|
||||
|
||||
:::tip identical twins
|
||||
If you have twins, you might want to lower the Max Recognition Distance value, decreasing this a **bit** can make it distinguish between them.
|
||||
:::
|
||||
|
||||
## Map & GPS Settings
|
||||
|
||||
### Map Settings
|
||||
|
||||
In these settings, you can change the appearance of the map in night and day modes according to your personal preference and according to the supported options.
|
||||
The map can be adjusted via [OpenMapTiles](https://openmaptiles.org/styles/) for example.
|
||||
|
||||
### Reverse Geocoding Settings
|
||||
|
||||
Immich supports [Reverse Geocoding](/docs/features/reverse-geocoding) using data from the [GeoNames](https://www.geonames.org/) geographical database.
|
||||
|
||||
## OAuth Authentication
|
||||
|
||||
Immich supports OAuth Authentication. Read more about this feature and its configuration [here](/docs/administration/oauth).
|
||||
|
||||
## Password Authentication
|
||||
|
||||
The administrator can choose to disable login with username and password for the entire instance. This means that **no one**, including the system administrator, will be able to log using this method. If [OAuth Authentication](/docs/administration/oauth) is also disabled, no users will be able to login using **any** method. Changing this setting does not affect existing sessions, just new login attempts.
|
||||
|
||||
:::tip
|
||||
You can always use the [Server CLI](/docs/administration/server-commands) to re-enable password login.
|
||||
:::
|
||||
|
||||
## Server Settings
|
||||
|
||||
### External Domain
|
||||
|
||||
When set, will override the domain name used when viewing and copying a shared link.
|
||||
|
||||
### Welcome Message
|
||||
|
||||
The administrator can set a custom message on the login screen (the message will be displayed to all users).
|
||||
|
||||
## Storage Template
|
||||
|
||||
Immich supports a custom [Storage Template](/docs/administration/storage-template). Learn more about this feature and its configuration [here](/docs/administration/storage-template).
|
||||
|
||||
## Theme Settings
|
||||
|
||||
You can write custom CSS that will get loaded in the web application for all users. This enables administrators to change fonts, colors, and other styles.
|
||||
|
||||
For example:
|
||||
|
||||
```css title='Custom CSS'
|
||||
p {
|
||||
color: green;
|
||||
}
|
||||
```
|
||||
|
||||
## Thumbnail Settings
|
||||
|
||||
By default Immich creates 3 thumbnails for each asset,
|
||||
Blurred (thumbhash) , Small (webp) , and Large (jpeg), using these settings you can change the quality for the thumbnail files that are created.
|
||||
|
||||
**Small thumbnail resolution**
|
||||
Used when viewing groups of photos (main timeline, album view, etc.). Higher resolutions can preserve more detail but take longer to encode, have larger file sizes, and can reduce app responsiveness.
|
||||
|
||||
**Large thumbnail resolution**
|
||||
Used when viewing a single photo and for machine learning. Higher resolutions can preserve more detail but take longer to encode, have larger file sizes, and can reduce app responsiveness.
|
||||
|
||||
**Quality**
|
||||
Thumbnail quality from 1-100. Higher is better for quality but produces larger files.
|
||||
|
||||
**Prefer wide gamut**
|
||||
Use display p3 for thumbnails. This better preserves the vibrance of images with wide color spaces, but images may appear differently on old devices with an old browser version. Srgb images are kept as srgb to avoid color shifts.
|
||||
|
||||
:::tip
|
||||
The default resolution for Large thumbnails can be lowered from 1440p (default) to 1080p or 720p to save storage space.
|
||||
:::
|
||||
|
||||
## Trash Settings
|
||||
|
||||
In the system administrator's option to set a trash for deleted files, these files will remain in the trash until the deletion date 30 days (default) or as defined by the system administrator.
|
||||
|
||||
The trash can be disabled, however this is not recommended as future files that are deleted will be permanently deleted.
|
||||
|
||||
:::tip Keyboard shortcut for permanently deletion
|
||||
You can select assets and press Ctrl + Del from the timeline for quick permanent deletion without the trash option.
|
||||
:::
|
||||
|
||||
## User Settings
|
||||
|
||||
### Delete delay
|
||||
|
||||
The system administrator can choose to delete users through the administration panel, the system administrator can delete users immediately or alternatively delay the deletion for users (7 days by default) this action permanently delete a user's account and assets. The user deletion job runs at midnight to check for users that are ready for deletion. Changes to this setting will be evaluated at the next execution.
|
||||
|
||||
## Version Check
|
||||
|
||||
When this option is enabled the `immich-server` will periodically make requests to GitHub to check for new releases.
|
||||
|
||||
## Video Transcoding Settings
|
||||
|
||||
The system administrator can define parameters according to which video files will be converted to different formats (depending on the settings). The settings can be changed in depth, to learn more about the terminology used here, refer to FFmpeg documentation for [H.264](https://trac.ffmpeg.org/wiki/Encode/H.264) codec, [HEVC](https://trac.ffmpeg.org/wiki/Encode/H.265) codec and [VP9](https://trac.ffmpeg.org/wiki/Encode/VP9) codec.
|
||||
@@ -45,7 +45,7 @@ SELECT * FROM "assets" JOIN "exif" ON "assets"."id" = "exif"."assetId" WHERE "ex
|
||||
```
|
||||
|
||||
```sql title="Without thumbnails"
|
||||
SELECT * FROM "assets" WHERE "assets"."previewPath" IS NULL OR "assets"."thumbnailPath" IS NULL;
|
||||
SELECT * FROM "assets" WHERE "assets"."resizePath" IS NULL OR "assets"."webpPath" IS NULL;
|
||||
```
|
||||
|
||||
```sql title="By type"
|
||||
|
||||
@@ -115,10 +115,8 @@ The default configuration looks like this:
|
||||
"template": "{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}"
|
||||
},
|
||||
"thumbnail": {
|
||||
"thumbnailFormat": "webp",
|
||||
"thumbnailSize": 250,
|
||||
"previewFormat": "jpeg",
|
||||
"previewSize": 1440,
|
||||
"webpSize": 250,
|
||||
"jpegSize": 1440,
|
||||
"quality": 80,
|
||||
"colorspace": "p3"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
Immich allows the admin user to set the uploaded filename pattern. Both at the directory and filename level.
|
||||
|
||||
:::note new version
|
||||
On new machines running version 1.92.0 storage template engine is off by default, for [more info](https://github.com/immich-app/immich/releases/tag/v1.92.0#:~:text=the%20partner%E2%80%99s%20assets.-,Hardening%20storage%20template,-We%20have%20further).
|
||||
On new machines running version 1.92.0 storage template engine is off by default, for [more info](https://github.com/immich-app/immich/releases#:~:text=the%20partner%E2%80%99s%20assets.-,Hardening%20storage%20template,-We%20have%20further).
|
||||
:::
|
||||
|
||||
:::tip
|
||||
|
||||
Vendored
-1
@@ -24,4 +24,3 @@
|
||||
/docs/features/user-management /docs/administration/user-management 301
|
||||
/docs/developer/contributing /docs/developer/pr-checklist 301
|
||||
/docs/guides/machine-learning /docs/guides/remote-machine-learning 301
|
||||
/docs/administration/password-login /docs/administration/system-settings 301
|
||||
|
||||
Generated
-3
@@ -72,7 +72,6 @@ doc/FileChecksumResponseDto.md
|
||||
doc/FileReportDto.md
|
||||
doc/FileReportFixDto.md
|
||||
doc/FileReportItemDto.md
|
||||
doc/ImageFormat.md
|
||||
doc/JobApi.md
|
||||
doc/JobCommand.md
|
||||
doc/JobCommandDto.md
|
||||
@@ -283,7 +282,6 @@ lib/model/file_checksum_response_dto.dart
|
||||
lib/model/file_report_dto.dart
|
||||
lib/model/file_report_fix_dto.dart
|
||||
lib/model/file_report_item_dto.dart
|
||||
lib/model/image_format.dart
|
||||
lib/model/job_command.dart
|
||||
lib/model/job_command_dto.dart
|
||||
lib/model/job_counts_dto.dart
|
||||
@@ -461,7 +459,6 @@ test/file_checksum_response_dto_test.dart
|
||||
test/file_report_dto_test.dart
|
||||
test/file_report_fix_dto_test.dart
|
||||
test/file_report_item_dto_test.dart
|
||||
test/image_format_test.dart
|
||||
test/job_api_test.dart
|
||||
test/job_command_dto_test.dart
|
||||
test/job_command_test.dart
|
||||
|
||||
Generated
-1
@@ -277,7 +277,6 @@ Class | Method | HTTP request | Description
|
||||
- [FileReportDto](doc//FileReportDto.md)
|
||||
- [FileReportFixDto](doc//FileReportFixDto.md)
|
||||
- [FileReportItemDto](doc//FileReportItemDto.md)
|
||||
- [ImageFormat](doc//ImageFormat.md)
|
||||
- [JobCommand](doc//JobCommand.md)
|
||||
- [JobCommandDto](doc//JobCommandDto.md)
|
||||
- [JobCountsDto](doc//JobCountsDto.md)
|
||||
|
||||
Generated
+6
-6
@@ -1040,7 +1040,7 @@ void (empty response body)
|
||||
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
|
||||
|
||||
# **searchAssets**
|
||||
> List<AssetResponseDto> searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, previewPath, size, state, takenAfter, takenBefore, thumbnailPath, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, withArchived, withDeleted, withExif, withPeople, withStacked)
|
||||
> List<AssetResponseDto> searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked)
|
||||
|
||||
|
||||
|
||||
@@ -1090,17 +1090,17 @@ final originalFileName = originalFileName_example; // String |
|
||||
final originalPath = originalPath_example; // String |
|
||||
final page = 8.14; // num |
|
||||
final personIds = []; // List<String> |
|
||||
final previewPath = previewPath_example; // String |
|
||||
final resizePath = resizePath_example; // String |
|
||||
final size = 8.14; // num |
|
||||
final state = state_example; // String |
|
||||
final takenAfter = 2013-10-20T19:20:30+01:00; // DateTime |
|
||||
final takenBefore = 2013-10-20T19:20:30+01:00; // DateTime |
|
||||
final thumbnailPath = thumbnailPath_example; // String |
|
||||
final trashedAfter = 2013-10-20T19:20:30+01:00; // DateTime |
|
||||
final trashedBefore = 2013-10-20T19:20:30+01:00; // DateTime |
|
||||
final type = ; // AssetTypeEnum |
|
||||
final updatedAfter = 2013-10-20T19:20:30+01:00; // DateTime |
|
||||
final updatedBefore = 2013-10-20T19:20:30+01:00; // DateTime |
|
||||
final webpPath = webpPath_example; // String |
|
||||
final withArchived = true; // bool |
|
||||
final withDeleted = true; // bool |
|
||||
final withExif = true; // bool |
|
||||
@@ -1108,7 +1108,7 @@ final withPeople = true; // bool |
|
||||
final withStacked = true; // bool |
|
||||
|
||||
try {
|
||||
final result = api_instance.searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, previewPath, size, state, takenAfter, takenBefore, thumbnailPath, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, withArchived, withDeleted, withExif, withPeople, withStacked);
|
||||
final result = api_instance.searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling AssetApi->searchAssets: $e\n');
|
||||
@@ -1146,17 +1146,17 @@ Name | Type | Description | Notes
|
||||
**originalPath** | **String**| | [optional]
|
||||
**page** | **num**| | [optional]
|
||||
**personIds** | [**List<String>**](String.md)| | [optional] [default to const []]
|
||||
**previewPath** | **String**| | [optional]
|
||||
**resizePath** | **String**| | [optional]
|
||||
**size** | **num**| | [optional]
|
||||
**state** | **String**| | [optional]
|
||||
**takenAfter** | **DateTime**| | [optional]
|
||||
**takenBefore** | **DateTime**| | [optional]
|
||||
**thumbnailPath** | **String**| | [optional]
|
||||
**trashedAfter** | **DateTime**| | [optional]
|
||||
**trashedBefore** | **DateTime**| | [optional]
|
||||
**type** | [**AssetTypeEnum**](.md)| | [optional]
|
||||
**updatedAfter** | **DateTime**| | [optional]
|
||||
**updatedBefore** | **DateTime**| | [optional]
|
||||
**webpPath** | **String**| | [optional]
|
||||
**withArchived** | **bool**| | [optional] [default to false]
|
||||
**withDeleted** | **bool**| | [optional]
|
||||
**withExif** | **bool**| | [optional]
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ Name | Type | Description | Notes
|
||||
**deviceAssetId** | **String** | |
|
||||
**deviceId** | **String** | |
|
||||
**id** | **String** | |
|
||||
**previewPath** | **String** | |
|
||||
**resizePath** | **String** | |
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ Name | Type | Description | Notes
|
||||
**deviceId** | **String** | |
|
||||
**id** | **String** | |
|
||||
**object** | **String** | |
|
||||
**previewPath** | **String** | |
|
||||
**resizePath** | **String** | |
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
Generated
-14
@@ -1,14 +0,0 @@
|
||||
# openapi.model.ImageFormat
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
Generated
+2
-2
@@ -35,17 +35,17 @@ Name | Type | Description | Notes
|
||||
**originalPath** | **String** | | [optional]
|
||||
**page** | **num** | | [optional]
|
||||
**personIds** | **List<String>** | | [optional] [default to const []]
|
||||
**previewPath** | **String** | | [optional]
|
||||
**resizePath** | **String** | | [optional]
|
||||
**size** | **num** | | [optional]
|
||||
**state** | **String** | | [optional]
|
||||
**takenAfter** | [**DateTime**](DateTime.md) | | [optional]
|
||||
**takenBefore** | [**DateTime**](DateTime.md) | | [optional]
|
||||
**thumbnailPath** | **String** | | [optional]
|
||||
**trashedAfter** | [**DateTime**](DateTime.md) | | [optional]
|
||||
**trashedBefore** | [**DateTime**](DateTime.md) | | [optional]
|
||||
**type** | [**AssetTypeEnum**](AssetTypeEnum.md) | | [optional]
|
||||
**updatedAfter** | [**DateTime**](DateTime.md) | | [optional]
|
||||
**updatedBefore** | [**DateTime**](DateTime.md) | | [optional]
|
||||
**webpPath** | **String** | | [optional]
|
||||
**withArchived** | **bool** | | [optional] [default to false]
|
||||
**withDeleted** | **bool** | | [optional]
|
||||
**withExif** | **bool** | | [optional]
|
||||
|
||||
Generated
+1
-1
@@ -9,7 +9,6 @@ import 'package:openapi/api.dart';
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**ffmpeg** | [**SystemConfigFFmpegDto**](SystemConfigFFmpegDto.md) | |
|
||||
**image** | [**SystemConfigThumbnailDto**](SystemConfigThumbnailDto.md) | |
|
||||
**job** | [**SystemConfigJobDto**](SystemConfigJobDto.md) | |
|
||||
**library_** | [**SystemConfigLibraryDto**](SystemConfigLibraryDto.md) | |
|
||||
**logging** | [**SystemConfigLoggingDto**](SystemConfigLoggingDto.md) | |
|
||||
@@ -22,6 +21,7 @@ Name | Type | Description | Notes
|
||||
**server** | [**SystemConfigServerDto**](SystemConfigServerDto.md) | |
|
||||
**storageTemplate** | [**SystemConfigStorageTemplateDto**](SystemConfigStorageTemplateDto.md) | |
|
||||
**theme** | [**SystemConfigThemeDto**](SystemConfigThemeDto.md) | |
|
||||
**thumbnail** | [**SystemConfigThumbnailDto**](SystemConfigThumbnailDto.md) | |
|
||||
**trash** | [**SystemConfigTrashDto**](SystemConfigTrashDto.md) | |
|
||||
**user** | [**SystemConfigUserDto**](SystemConfigUserDto.md) | |
|
||||
|
||||
|
||||
+2
-4
@@ -9,11 +9,9 @@ import 'package:openapi/api.dart';
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**colorspace** | [**Colorspace**](Colorspace.md) | |
|
||||
**previewFormat** | [**ImageFormat**](ImageFormat.md) | |
|
||||
**previewSize** | **int** | |
|
||||
**jpegSize** | **int** | |
|
||||
**quality** | **int** | |
|
||||
**thumbnailFormat** | [**ImageFormat**](ImageFormat.md) | |
|
||||
**thumbnailSize** | **int** | |
|
||||
**webpSize** | **int** | |
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
Generated
-1
@@ -111,7 +111,6 @@ part 'model/file_checksum_response_dto.dart';
|
||||
part 'model/file_report_dto.dart';
|
||||
part 'model/file_report_fix_dto.dart';
|
||||
part 'model/file_report_item_dto.dart';
|
||||
part 'model/image_format.dart';
|
||||
part 'model/job_command.dart';
|
||||
part 'model/job_command_dto.dart';
|
||||
part 'model/job_counts_dto.dart';
|
||||
|
||||
Generated
+14
-14
@@ -1180,7 +1180,7 @@ class AssetApi {
|
||||
///
|
||||
/// * [List<String>] personIds:
|
||||
///
|
||||
/// * [String] previewPath:
|
||||
/// * [String] resizePath:
|
||||
///
|
||||
/// * [num] size:
|
||||
///
|
||||
@@ -1190,8 +1190,6 @@ class AssetApi {
|
||||
///
|
||||
/// * [DateTime] takenBefore:
|
||||
///
|
||||
/// * [String] thumbnailPath:
|
||||
///
|
||||
/// * [DateTime] trashedAfter:
|
||||
///
|
||||
/// * [DateTime] trashedBefore:
|
||||
@@ -1202,6 +1200,8 @@ class AssetApi {
|
||||
///
|
||||
/// * [DateTime] updatedBefore:
|
||||
///
|
||||
/// * [String] webpPath:
|
||||
///
|
||||
/// * [bool] withArchived:
|
||||
///
|
||||
/// * [bool] withDeleted:
|
||||
@@ -1211,7 +1211,7 @@ class AssetApi {
|
||||
/// * [bool] withPeople:
|
||||
///
|
||||
/// * [bool] withStacked:
|
||||
Future<Response> searchAssetsWithHttpInfo({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, List<String>? personIds, String? previewPath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, String? thumbnailPath, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async {
|
||||
Future<Response> searchAssetsWithHttpInfo({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, List<String>? personIds, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/assets';
|
||||
|
||||
@@ -1303,8 +1303,8 @@ class AssetApi {
|
||||
if (personIds != null) {
|
||||
queryParams.addAll(_queryParams('multi', 'personIds', personIds));
|
||||
}
|
||||
if (previewPath != null) {
|
||||
queryParams.addAll(_queryParams('', 'previewPath', previewPath));
|
||||
if (resizePath != null) {
|
||||
queryParams.addAll(_queryParams('', 'resizePath', resizePath));
|
||||
}
|
||||
if (size != null) {
|
||||
queryParams.addAll(_queryParams('', 'size', size));
|
||||
@@ -1318,9 +1318,6 @@ class AssetApi {
|
||||
if (takenBefore != null) {
|
||||
queryParams.addAll(_queryParams('', 'takenBefore', takenBefore));
|
||||
}
|
||||
if (thumbnailPath != null) {
|
||||
queryParams.addAll(_queryParams('', 'thumbnailPath', thumbnailPath));
|
||||
}
|
||||
if (trashedAfter != null) {
|
||||
queryParams.addAll(_queryParams('', 'trashedAfter', trashedAfter));
|
||||
}
|
||||
@@ -1336,6 +1333,9 @@ class AssetApi {
|
||||
if (updatedBefore != null) {
|
||||
queryParams.addAll(_queryParams('', 'updatedBefore', updatedBefore));
|
||||
}
|
||||
if (webpPath != null) {
|
||||
queryParams.addAll(_queryParams('', 'webpPath', webpPath));
|
||||
}
|
||||
if (withArchived != null) {
|
||||
queryParams.addAll(_queryParams('', 'withArchived', withArchived));
|
||||
}
|
||||
@@ -1422,7 +1422,7 @@ class AssetApi {
|
||||
///
|
||||
/// * [List<String>] personIds:
|
||||
///
|
||||
/// * [String] previewPath:
|
||||
/// * [String] resizePath:
|
||||
///
|
||||
/// * [num] size:
|
||||
///
|
||||
@@ -1432,8 +1432,6 @@ class AssetApi {
|
||||
///
|
||||
/// * [DateTime] takenBefore:
|
||||
///
|
||||
/// * [String] thumbnailPath:
|
||||
///
|
||||
/// * [DateTime] trashedAfter:
|
||||
///
|
||||
/// * [DateTime] trashedBefore:
|
||||
@@ -1444,6 +1442,8 @@ class AssetApi {
|
||||
///
|
||||
/// * [DateTime] updatedBefore:
|
||||
///
|
||||
/// * [String] webpPath:
|
||||
///
|
||||
/// * [bool] withArchived:
|
||||
///
|
||||
/// * [bool] withDeleted:
|
||||
@@ -1453,8 +1453,8 @@ class AssetApi {
|
||||
/// * [bool] withPeople:
|
||||
///
|
||||
/// * [bool] withStacked:
|
||||
Future<List<AssetResponseDto>?> searchAssets({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, List<String>? personIds, String? previewPath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, String? thumbnailPath, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async {
|
||||
final response = await searchAssetsWithHttpInfo( checksum: checksum, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceAssetId: deviceAssetId, deviceId: deviceId, encodedVideoPath: encodedVideoPath, id: id, isArchived: isArchived, isEncoded: isEncoded, isExternal: isExternal, isFavorite: isFavorite, isMotion: isMotion, isNotInAlbum: isNotInAlbum, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, lensModel: lensModel, libraryId: libraryId, make: make, model: model, order: order, originalFileName: originalFileName, originalPath: originalPath, page: page, personIds: personIds, previewPath: previewPath, size: size, state: state, takenAfter: takenAfter, takenBefore: takenBefore, thumbnailPath: thumbnailPath, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, withArchived: withArchived, withDeleted: withDeleted, withExif: withExif, withPeople: withPeople, withStacked: withStacked, );
|
||||
Future<List<AssetResponseDto>?> searchAssets({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, List<String>? personIds, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async {
|
||||
final response = await searchAssetsWithHttpInfo( checksum: checksum, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceAssetId: deviceAssetId, deviceId: deviceId, encodedVideoPath: encodedVideoPath, id: id, isArchived: isArchived, isEncoded: isEncoded, isExternal: isExternal, isFavorite: isFavorite, isMotion: isMotion, isNotInAlbum: isNotInAlbum, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, lensModel: lensModel, libraryId: libraryId, make: make, model: model, order: order, originalFileName: originalFileName, originalPath: originalPath, page: page, personIds: personIds, resizePath: resizePath, size: size, state: state, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, webpPath: webpPath, withArchived: withArchived, withDeleted: withDeleted, withExif: withExif, withPeople: withPeople, withStacked: withStacked, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
|
||||
Generated
-2
@@ -304,8 +304,6 @@ class ApiClient {
|
||||
return FileReportFixDto.fromJson(value);
|
||||
case 'FileReportItemDto':
|
||||
return FileReportItemDto.fromJson(value);
|
||||
case 'ImageFormat':
|
||||
return ImageFormatTypeTransformer().decode(value);
|
||||
case 'JobCommand':
|
||||
return JobCommandTypeTransformer().decode(value);
|
||||
case 'JobCommandDto':
|
||||
|
||||
Generated
-3
@@ -79,9 +79,6 @@ String parameterToString(dynamic value) {
|
||||
if (value is EntityType) {
|
||||
return EntityTypeTypeTransformer().encode(value).toString();
|
||||
}
|
||||
if (value is ImageFormat) {
|
||||
return ImageFormatTypeTransformer().encode(value).toString();
|
||||
}
|
||||
if (value is JobCommand) {
|
||||
return JobCommandTypeTransformer().encode(value).toString();
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ class CuratedLocationsResponseDto {
|
||||
required this.deviceAssetId,
|
||||
required this.deviceId,
|
||||
required this.id,
|
||||
required this.previewPath,
|
||||
required this.resizePath,
|
||||
});
|
||||
|
||||
String city;
|
||||
@@ -28,7 +28,7 @@ class CuratedLocationsResponseDto {
|
||||
|
||||
String id;
|
||||
|
||||
String previewPath;
|
||||
String resizePath;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is CuratedLocationsResponseDto &&
|
||||
@@ -36,7 +36,7 @@ class CuratedLocationsResponseDto {
|
||||
other.deviceAssetId == deviceAssetId &&
|
||||
other.deviceId == deviceId &&
|
||||
other.id == id &&
|
||||
other.previewPath == previewPath;
|
||||
other.resizePath == resizePath;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
@@ -45,10 +45,10 @@ class CuratedLocationsResponseDto {
|
||||
(deviceAssetId.hashCode) +
|
||||
(deviceId.hashCode) +
|
||||
(id.hashCode) +
|
||||
(previewPath.hashCode);
|
||||
(resizePath.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'CuratedLocationsResponseDto[city=$city, deviceAssetId=$deviceAssetId, deviceId=$deviceId, id=$id, previewPath=$previewPath]';
|
||||
String toString() => 'CuratedLocationsResponseDto[city=$city, deviceAssetId=$deviceAssetId, deviceId=$deviceId, id=$id, resizePath=$resizePath]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -56,7 +56,7 @@ class CuratedLocationsResponseDto {
|
||||
json[r'deviceAssetId'] = this.deviceAssetId;
|
||||
json[r'deviceId'] = this.deviceId;
|
||||
json[r'id'] = this.id;
|
||||
json[r'previewPath'] = this.previewPath;
|
||||
json[r'resizePath'] = this.resizePath;
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ class CuratedLocationsResponseDto {
|
||||
deviceAssetId: mapValueOfType<String>(json, r'deviceAssetId')!,
|
||||
deviceId: mapValueOfType<String>(json, r'deviceId')!,
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
previewPath: mapValueOfType<String>(json, r'previewPath')!,
|
||||
resizePath: mapValueOfType<String>(json, r'resizePath')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
@@ -124,7 +124,7 @@ class CuratedLocationsResponseDto {
|
||||
'deviceAssetId',
|
||||
'deviceId',
|
||||
'id',
|
||||
'previewPath',
|
||||
'resizePath',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+8
-8
@@ -17,7 +17,7 @@ class CuratedObjectsResponseDto {
|
||||
required this.deviceId,
|
||||
required this.id,
|
||||
required this.object,
|
||||
required this.previewPath,
|
||||
required this.resizePath,
|
||||
});
|
||||
|
||||
String deviceAssetId;
|
||||
@@ -28,7 +28,7 @@ class CuratedObjectsResponseDto {
|
||||
|
||||
String object;
|
||||
|
||||
String previewPath;
|
||||
String resizePath;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is CuratedObjectsResponseDto &&
|
||||
@@ -36,7 +36,7 @@ class CuratedObjectsResponseDto {
|
||||
other.deviceId == deviceId &&
|
||||
other.id == id &&
|
||||
other.object == object &&
|
||||
other.previewPath == previewPath;
|
||||
other.resizePath == resizePath;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
@@ -45,10 +45,10 @@ class CuratedObjectsResponseDto {
|
||||
(deviceId.hashCode) +
|
||||
(id.hashCode) +
|
||||
(object.hashCode) +
|
||||
(previewPath.hashCode);
|
||||
(resizePath.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'CuratedObjectsResponseDto[deviceAssetId=$deviceAssetId, deviceId=$deviceId, id=$id, object=$object, previewPath=$previewPath]';
|
||||
String toString() => 'CuratedObjectsResponseDto[deviceAssetId=$deviceAssetId, deviceId=$deviceId, id=$id, object=$object, resizePath=$resizePath]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -56,7 +56,7 @@ class CuratedObjectsResponseDto {
|
||||
json[r'deviceId'] = this.deviceId;
|
||||
json[r'id'] = this.id;
|
||||
json[r'object'] = this.object;
|
||||
json[r'previewPath'] = this.previewPath;
|
||||
json[r'resizePath'] = this.resizePath;
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ class CuratedObjectsResponseDto {
|
||||
deviceId: mapValueOfType<String>(json, r'deviceId')!,
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
object: mapValueOfType<String>(json, r'object')!,
|
||||
previewPath: mapValueOfType<String>(json, r'previewPath')!,
|
||||
resizePath: mapValueOfType<String>(json, r'resizePath')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
@@ -124,7 +124,7 @@ class CuratedObjectsResponseDto {
|
||||
'deviceId',
|
||||
'id',
|
||||
'object',
|
||||
'previewPath',
|
||||
'resizePath',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
-85
@@ -1,85 +0,0 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
part of openapi.api;
|
||||
|
||||
|
||||
class ImageFormat {
|
||||
/// Instantiate a new enum with the provided [value].
|
||||
const ImageFormat._(this.value);
|
||||
|
||||
/// The underlying value of this enum member.
|
||||
final String value;
|
||||
|
||||
@override
|
||||
String toString() => value;
|
||||
|
||||
String toJson() => value;
|
||||
|
||||
static const jpeg = ImageFormat._(r'jpeg');
|
||||
static const webp = ImageFormat._(r'webp');
|
||||
|
||||
/// List of all possible values in this [enum][ImageFormat].
|
||||
static const values = <ImageFormat>[
|
||||
jpeg,
|
||||
webp,
|
||||
];
|
||||
|
||||
static ImageFormat? fromJson(dynamic value) => ImageFormatTypeTransformer().decode(value);
|
||||
|
||||
static List<ImageFormat> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <ImageFormat>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = ImageFormat.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
}
|
||||
|
||||
/// Transformation class that can [encode] an instance of [ImageFormat] to String,
|
||||
/// and [decode] dynamic data back to [ImageFormat].
|
||||
class ImageFormatTypeTransformer {
|
||||
factory ImageFormatTypeTransformer() => _instance ??= const ImageFormatTypeTransformer._();
|
||||
|
||||
const ImageFormatTypeTransformer._();
|
||||
|
||||
String encode(ImageFormat data) => data.value;
|
||||
|
||||
/// Decodes a [dynamic value][data] to a ImageFormat.
|
||||
///
|
||||
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
||||
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
||||
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
|
||||
///
|
||||
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
||||
/// and users are still using an old app with the old code.
|
||||
ImageFormat? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'jpeg': return ImageFormat.jpeg;
|
||||
case r'webp': return ImageFormat.webp;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Singleton [ImageFormatTypeTransformer] instance.
|
||||
static ImageFormatTypeTransformer? _instance;
|
||||
}
|
||||
|
||||
+26
-26
@@ -40,17 +40,17 @@ class MetadataSearchDto {
|
||||
this.originalPath,
|
||||
this.page,
|
||||
this.personIds = const [],
|
||||
this.previewPath,
|
||||
this.resizePath,
|
||||
this.size,
|
||||
this.state,
|
||||
this.takenAfter,
|
||||
this.takenBefore,
|
||||
this.thumbnailPath,
|
||||
this.trashedAfter,
|
||||
this.trashedBefore,
|
||||
this.type,
|
||||
this.updatedAfter,
|
||||
this.updatedBefore,
|
||||
this.webpPath,
|
||||
this.withArchived = false,
|
||||
this.withDeleted,
|
||||
this.withExif,
|
||||
@@ -274,7 +274,7 @@ class MetadataSearchDto {
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? previewPath;
|
||||
String? resizePath;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
@@ -308,14 +308,6 @@ class MetadataSearchDto {
|
||||
///
|
||||
DateTime? takenBefore;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? thumbnailPath;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
@@ -356,6 +348,14 @@ class MetadataSearchDto {
|
||||
///
|
||||
DateTime? updatedBefore;
|
||||
|
||||
///
|
||||
/// Please note: This property should have been non-nullable! Since the specification file
|
||||
/// does not include a default value (using the "default:" property), however, the generated
|
||||
/// source code must fall back to having a nullable type.
|
||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||
///
|
||||
String? webpPath;
|
||||
|
||||
bool withArchived;
|
||||
|
||||
///
|
||||
@@ -419,17 +419,17 @@ class MetadataSearchDto {
|
||||
other.originalPath == originalPath &&
|
||||
other.page == page &&
|
||||
_deepEquality.equals(other.personIds, personIds) &&
|
||||
other.previewPath == previewPath &&
|
||||
other.resizePath == resizePath &&
|
||||
other.size == size &&
|
||||
other.state == state &&
|
||||
other.takenAfter == takenAfter &&
|
||||
other.takenBefore == takenBefore &&
|
||||
other.thumbnailPath == thumbnailPath &&
|
||||
other.trashedAfter == trashedAfter &&
|
||||
other.trashedBefore == trashedBefore &&
|
||||
other.type == type &&
|
||||
other.updatedAfter == updatedAfter &&
|
||||
other.updatedBefore == updatedBefore &&
|
||||
other.webpPath == webpPath &&
|
||||
other.withArchived == withArchived &&
|
||||
other.withDeleted == withDeleted &&
|
||||
other.withExif == withExif &&
|
||||
@@ -466,17 +466,17 @@ class MetadataSearchDto {
|
||||
(originalPath == null ? 0 : originalPath!.hashCode) +
|
||||
(page == null ? 0 : page!.hashCode) +
|
||||
(personIds.hashCode) +
|
||||
(previewPath == null ? 0 : previewPath!.hashCode) +
|
||||
(resizePath == null ? 0 : resizePath!.hashCode) +
|
||||
(size == null ? 0 : size!.hashCode) +
|
||||
(state == null ? 0 : state!.hashCode) +
|
||||
(takenAfter == null ? 0 : takenAfter!.hashCode) +
|
||||
(takenBefore == null ? 0 : takenBefore!.hashCode) +
|
||||
(thumbnailPath == null ? 0 : thumbnailPath!.hashCode) +
|
||||
(trashedAfter == null ? 0 : trashedAfter!.hashCode) +
|
||||
(trashedBefore == null ? 0 : trashedBefore!.hashCode) +
|
||||
(type == null ? 0 : type!.hashCode) +
|
||||
(updatedAfter == null ? 0 : updatedAfter!.hashCode) +
|
||||
(updatedBefore == null ? 0 : updatedBefore!.hashCode) +
|
||||
(webpPath == null ? 0 : webpPath!.hashCode) +
|
||||
(withArchived.hashCode) +
|
||||
(withDeleted == null ? 0 : withDeleted!.hashCode) +
|
||||
(withExif == null ? 0 : withExif!.hashCode) +
|
||||
@@ -484,7 +484,7 @@ class MetadataSearchDto {
|
||||
(withStacked == null ? 0 : withStacked!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'MetadataSearchDto[checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceAssetId=$deviceAssetId, deviceId=$deviceId, encodedVideoPath=$encodedVideoPath, id=$id, isArchived=$isArchived, isEncoded=$isEncoded, isExternal=$isExternal, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isReadOnly=$isReadOnly, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, order=$order, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, previewPath=$previewPath, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, thumbnailPath=$thumbnailPath, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]';
|
||||
String toString() => 'MetadataSearchDto[checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceAssetId=$deviceAssetId, deviceId=$deviceId, encodedVideoPath=$encodedVideoPath, id=$id, isArchived=$isArchived, isEncoded=$isEncoded, isExternal=$isExternal, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isReadOnly=$isReadOnly, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, order=$order, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, resizePath=$resizePath, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, webpPath=$webpPath, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -619,10 +619,10 @@ class MetadataSearchDto {
|
||||
// json[r'page'] = null;
|
||||
}
|
||||
json[r'personIds'] = this.personIds;
|
||||
if (this.previewPath != null) {
|
||||
json[r'previewPath'] = this.previewPath;
|
||||
if (this.resizePath != null) {
|
||||
json[r'resizePath'] = this.resizePath;
|
||||
} else {
|
||||
// json[r'previewPath'] = null;
|
||||
// json[r'resizePath'] = null;
|
||||
}
|
||||
if (this.size != null) {
|
||||
json[r'size'] = this.size;
|
||||
@@ -644,11 +644,6 @@ class MetadataSearchDto {
|
||||
} else {
|
||||
// json[r'takenBefore'] = null;
|
||||
}
|
||||
if (this.thumbnailPath != null) {
|
||||
json[r'thumbnailPath'] = this.thumbnailPath;
|
||||
} else {
|
||||
// json[r'thumbnailPath'] = null;
|
||||
}
|
||||
if (this.trashedAfter != null) {
|
||||
json[r'trashedAfter'] = this.trashedAfter!.toUtc().toIso8601String();
|
||||
} else {
|
||||
@@ -673,6 +668,11 @@ class MetadataSearchDto {
|
||||
json[r'updatedBefore'] = this.updatedBefore!.toUtc().toIso8601String();
|
||||
} else {
|
||||
// json[r'updatedBefore'] = null;
|
||||
}
|
||||
if (this.webpPath != null) {
|
||||
json[r'webpPath'] = this.webpPath;
|
||||
} else {
|
||||
// json[r'webpPath'] = null;
|
||||
}
|
||||
json[r'withArchived'] = this.withArchived;
|
||||
if (this.withDeleted != null) {
|
||||
@@ -735,17 +735,17 @@ class MetadataSearchDto {
|
||||
personIds: json[r'personIds'] is Iterable
|
||||
? (json[r'personIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
previewPath: mapValueOfType<String>(json, r'previewPath'),
|
||||
resizePath: mapValueOfType<String>(json, r'resizePath'),
|
||||
size: num.parse('${json[r'size']}'),
|
||||
state: mapValueOfType<String>(json, r'state'),
|
||||
takenAfter: mapDateTime(json, r'takenAfter', r''),
|
||||
takenBefore: mapDateTime(json, r'takenBefore', r''),
|
||||
thumbnailPath: mapValueOfType<String>(json, r'thumbnailPath'),
|
||||
trashedAfter: mapDateTime(json, r'trashedAfter', r''),
|
||||
trashedBefore: mapDateTime(json, r'trashedBefore', r''),
|
||||
type: AssetTypeEnum.fromJson(json[r'type']),
|
||||
updatedAfter: mapDateTime(json, r'updatedAfter', r''),
|
||||
updatedBefore: mapDateTime(json, r'updatedBefore', r''),
|
||||
webpPath: mapValueOfType<String>(json, r'webpPath'),
|
||||
withArchived: mapValueOfType<bool>(json, r'withArchived') ?? false,
|
||||
withDeleted: mapValueOfType<bool>(json, r'withDeleted'),
|
||||
withExif: mapValueOfType<bool>(json, r'withExif'),
|
||||
|
||||
Generated
+6
-6
@@ -24,8 +24,8 @@ class PathType {
|
||||
String toJson() => value;
|
||||
|
||||
static const original = PathType._(r'original');
|
||||
static const preview = PathType._(r'preview');
|
||||
static const thumbnail = PathType._(r'thumbnail');
|
||||
static const jpegThumbnail = PathType._(r'jpeg_thumbnail');
|
||||
static const webpThumbnail = PathType._(r'webp_thumbnail');
|
||||
static const encodedVideo = PathType._(r'encoded_video');
|
||||
static const sidecar = PathType._(r'sidecar');
|
||||
static const face = PathType._(r'face');
|
||||
@@ -34,8 +34,8 @@ class PathType {
|
||||
/// List of all possible values in this [enum][PathType].
|
||||
static const values = <PathType>[
|
||||
original,
|
||||
preview,
|
||||
thumbnail,
|
||||
jpegThumbnail,
|
||||
webpThumbnail,
|
||||
encodedVideo,
|
||||
sidecar,
|
||||
face,
|
||||
@@ -79,8 +79,8 @@ class PathTypeTypeTransformer {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'original': return PathType.original;
|
||||
case r'preview': return PathType.preview;
|
||||
case r'thumbnail': return PathType.thumbnail;
|
||||
case r'jpeg_thumbnail': return PathType.jpegThumbnail;
|
||||
case r'webp_thumbnail': return PathType.webpThumbnail;
|
||||
case r'encoded_video': return PathType.encodedVideo;
|
||||
case r'sidecar': return PathType.sidecar;
|
||||
case r'face': return PathType.face;
|
||||
|
||||
+9
-9
@@ -14,7 +14,6 @@ class SystemConfigDto {
|
||||
/// Returns a new [SystemConfigDto] instance.
|
||||
SystemConfigDto({
|
||||
required this.ffmpeg,
|
||||
required this.image,
|
||||
required this.job,
|
||||
required this.library_,
|
||||
required this.logging,
|
||||
@@ -27,14 +26,13 @@ class SystemConfigDto {
|
||||
required this.server,
|
||||
required this.storageTemplate,
|
||||
required this.theme,
|
||||
required this.thumbnail,
|
||||
required this.trash,
|
||||
required this.user,
|
||||
});
|
||||
|
||||
SystemConfigFFmpegDto ffmpeg;
|
||||
|
||||
SystemConfigThumbnailDto image;
|
||||
|
||||
SystemConfigJobDto job;
|
||||
|
||||
SystemConfigLibraryDto library_;
|
||||
@@ -59,6 +57,8 @@ class SystemConfigDto {
|
||||
|
||||
SystemConfigThemeDto theme;
|
||||
|
||||
SystemConfigThumbnailDto thumbnail;
|
||||
|
||||
SystemConfigTrashDto trash;
|
||||
|
||||
SystemConfigUserDto user;
|
||||
@@ -66,7 +66,6 @@ class SystemConfigDto {
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SystemConfigDto &&
|
||||
other.ffmpeg == ffmpeg &&
|
||||
other.image == image &&
|
||||
other.job == job &&
|
||||
other.library_ == library_ &&
|
||||
other.logging == logging &&
|
||||
@@ -79,6 +78,7 @@ class SystemConfigDto {
|
||||
other.server == server &&
|
||||
other.storageTemplate == storageTemplate &&
|
||||
other.theme == theme &&
|
||||
other.thumbnail == thumbnail &&
|
||||
other.trash == trash &&
|
||||
other.user == user;
|
||||
|
||||
@@ -86,7 +86,6 @@ class SystemConfigDto {
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(ffmpeg.hashCode) +
|
||||
(image.hashCode) +
|
||||
(job.hashCode) +
|
||||
(library_.hashCode) +
|
||||
(logging.hashCode) +
|
||||
@@ -99,16 +98,16 @@ class SystemConfigDto {
|
||||
(server.hashCode) +
|
||||
(storageTemplate.hashCode) +
|
||||
(theme.hashCode) +
|
||||
(thumbnail.hashCode) +
|
||||
(trash.hashCode) +
|
||||
(user.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, image=$image, job=$job, library_=$library_, logging=$logging, machineLearning=$machineLearning, map=$map, newVersionCheck=$newVersionCheck, oauth=$oauth, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, server=$server, storageTemplate=$storageTemplate, theme=$theme, trash=$trash, user=$user]';
|
||||
String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, job=$job, library_=$library_, logging=$logging, machineLearning=$machineLearning, map=$map, newVersionCheck=$newVersionCheck, oauth=$oauth, passwordLogin=$passwordLogin, reverseGeocoding=$reverseGeocoding, server=$server, storageTemplate=$storageTemplate, theme=$theme, thumbnail=$thumbnail, trash=$trash, user=$user]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'ffmpeg'] = this.ffmpeg;
|
||||
json[r'image'] = this.image;
|
||||
json[r'job'] = this.job;
|
||||
json[r'library'] = this.library_;
|
||||
json[r'logging'] = this.logging;
|
||||
@@ -121,6 +120,7 @@ class SystemConfigDto {
|
||||
json[r'server'] = this.server;
|
||||
json[r'storageTemplate'] = this.storageTemplate;
|
||||
json[r'theme'] = this.theme;
|
||||
json[r'thumbnail'] = this.thumbnail;
|
||||
json[r'trash'] = this.trash;
|
||||
json[r'user'] = this.user;
|
||||
return json;
|
||||
@@ -135,7 +135,6 @@ class SystemConfigDto {
|
||||
|
||||
return SystemConfigDto(
|
||||
ffmpeg: SystemConfigFFmpegDto.fromJson(json[r'ffmpeg'])!,
|
||||
image: SystemConfigThumbnailDto.fromJson(json[r'image'])!,
|
||||
job: SystemConfigJobDto.fromJson(json[r'job'])!,
|
||||
library_: SystemConfigLibraryDto.fromJson(json[r'library'])!,
|
||||
logging: SystemConfigLoggingDto.fromJson(json[r'logging'])!,
|
||||
@@ -148,6 +147,7 @@ class SystemConfigDto {
|
||||
server: SystemConfigServerDto.fromJson(json[r'server'])!,
|
||||
storageTemplate: SystemConfigStorageTemplateDto.fromJson(json[r'storageTemplate'])!,
|
||||
theme: SystemConfigThemeDto.fromJson(json[r'theme'])!,
|
||||
thumbnail: SystemConfigThumbnailDto.fromJson(json[r'thumbnail'])!,
|
||||
trash: SystemConfigTrashDto.fromJson(json[r'trash'])!,
|
||||
user: SystemConfigUserDto.fromJson(json[r'user'])!,
|
||||
);
|
||||
@@ -198,7 +198,6 @@ class SystemConfigDto {
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'ffmpeg',
|
||||
'image',
|
||||
'job',
|
||||
'library',
|
||||
'logging',
|
||||
@@ -211,6 +210,7 @@ class SystemConfigDto {
|
||||
'server',
|
||||
'storageTemplate',
|
||||
'theme',
|
||||
'thumbnail',
|
||||
'trash',
|
||||
'user',
|
||||
};
|
||||
|
||||
+15
-31
@@ -14,55 +14,43 @@ class SystemConfigThumbnailDto {
|
||||
/// Returns a new [SystemConfigThumbnailDto] instance.
|
||||
SystemConfigThumbnailDto({
|
||||
required this.colorspace,
|
||||
required this.previewFormat,
|
||||
required this.previewSize,
|
||||
required this.jpegSize,
|
||||
required this.quality,
|
||||
required this.thumbnailFormat,
|
||||
required this.thumbnailSize,
|
||||
required this.webpSize,
|
||||
});
|
||||
|
||||
Colorspace colorspace;
|
||||
|
||||
ImageFormat previewFormat;
|
||||
|
||||
int previewSize;
|
||||
int jpegSize;
|
||||
|
||||
int quality;
|
||||
|
||||
ImageFormat thumbnailFormat;
|
||||
|
||||
int thumbnailSize;
|
||||
int webpSize;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SystemConfigThumbnailDto &&
|
||||
other.colorspace == colorspace &&
|
||||
other.previewFormat == previewFormat &&
|
||||
other.previewSize == previewSize &&
|
||||
other.jpegSize == jpegSize &&
|
||||
other.quality == quality &&
|
||||
other.thumbnailFormat == thumbnailFormat &&
|
||||
other.thumbnailSize == thumbnailSize;
|
||||
other.webpSize == webpSize;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(colorspace.hashCode) +
|
||||
(previewFormat.hashCode) +
|
||||
(previewSize.hashCode) +
|
||||
(jpegSize.hashCode) +
|
||||
(quality.hashCode) +
|
||||
(thumbnailFormat.hashCode) +
|
||||
(thumbnailSize.hashCode);
|
||||
(webpSize.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigThumbnailDto[colorspace=$colorspace, previewFormat=$previewFormat, previewSize=$previewSize, quality=$quality, thumbnailFormat=$thumbnailFormat, thumbnailSize=$thumbnailSize]';
|
||||
String toString() => 'SystemConfigThumbnailDto[colorspace=$colorspace, jpegSize=$jpegSize, quality=$quality, webpSize=$webpSize]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'colorspace'] = this.colorspace;
|
||||
json[r'previewFormat'] = this.previewFormat;
|
||||
json[r'previewSize'] = this.previewSize;
|
||||
json[r'jpegSize'] = this.jpegSize;
|
||||
json[r'quality'] = this.quality;
|
||||
json[r'thumbnailFormat'] = this.thumbnailFormat;
|
||||
json[r'thumbnailSize'] = this.thumbnailSize;
|
||||
json[r'webpSize'] = this.webpSize;
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -75,11 +63,9 @@ class SystemConfigThumbnailDto {
|
||||
|
||||
return SystemConfigThumbnailDto(
|
||||
colorspace: Colorspace.fromJson(json[r'colorspace'])!,
|
||||
previewFormat: ImageFormat.fromJson(json[r'previewFormat'])!,
|
||||
previewSize: mapValueOfType<int>(json, r'previewSize')!,
|
||||
jpegSize: mapValueOfType<int>(json, r'jpegSize')!,
|
||||
quality: mapValueOfType<int>(json, r'quality')!,
|
||||
thumbnailFormat: ImageFormat.fromJson(json[r'thumbnailFormat'])!,
|
||||
thumbnailSize: mapValueOfType<int>(json, r'thumbnailSize')!,
|
||||
webpSize: mapValueOfType<int>(json, r'webpSize')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
@@ -128,11 +114,9 @@ class SystemConfigThumbnailDto {
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'colorspace',
|
||||
'previewFormat',
|
||||
'previewSize',
|
||||
'jpegSize',
|
||||
'quality',
|
||||
'thumbnailFormat',
|
||||
'thumbnailSize',
|
||||
'webpSize',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
+6
-6
@@ -23,13 +23,13 @@ class ThumbnailFormat {
|
||||
|
||||
String toJson() => value;
|
||||
|
||||
static const jpeg = ThumbnailFormat._(r'jpeg');
|
||||
static const webp = ThumbnailFormat._(r'webp');
|
||||
static const JPEG = ThumbnailFormat._(r'JPEG');
|
||||
static const WEBP = ThumbnailFormat._(r'WEBP');
|
||||
|
||||
/// List of all possible values in this [enum][ThumbnailFormat].
|
||||
static const values = <ThumbnailFormat>[
|
||||
jpeg,
|
||||
webp,
|
||||
JPEG,
|
||||
WEBP,
|
||||
];
|
||||
|
||||
static ThumbnailFormat? fromJson(dynamic value) => ThumbnailFormatTypeTransformer().decode(value);
|
||||
@@ -68,8 +68,8 @@ class ThumbnailFormatTypeTransformer {
|
||||
ThumbnailFormat? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'jpeg': return ThumbnailFormat.jpeg;
|
||||
case r'webp': return ThumbnailFormat.webp;
|
||||
case r'JPEG': return ThumbnailFormat.JPEG;
|
||||
case r'WEBP': return ThumbnailFormat.WEBP;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
|
||||
Generated
+1
-1
@@ -110,7 +110,7 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<List<AssetResponseDto>> searchAssets({ String checksum, String city, String country, DateTime createdAfter, DateTime createdBefore, String deviceAssetId, String deviceId, String encodedVideoPath, String id, bool isArchived, bool isEncoded, bool isExternal, bool isFavorite, bool isMotion, bool isNotInAlbum, bool isOffline, bool isReadOnly, bool isVisible, String lensModel, String libraryId, String make, String model, AssetOrder order, String originalFileName, String originalPath, num page, List<String> personIds, String previewPath, num size, String state, DateTime takenAfter, DateTime takenBefore, String thumbnailPath, DateTime trashedAfter, DateTime trashedBefore, AssetTypeEnum type, DateTime updatedAfter, DateTime updatedBefore, bool withArchived, bool withDeleted, bool withExif, bool withPeople, bool withStacked }) async
|
||||
//Future<List<AssetResponseDto>> searchAssets({ String checksum, String city, String country, DateTime createdAfter, DateTime createdBefore, String deviceAssetId, String deviceId, String encodedVideoPath, String id, bool isArchived, bool isEncoded, bool isExternal, bool isFavorite, bool isMotion, bool isNotInAlbum, bool isOffline, bool isReadOnly, bool isVisible, String lensModel, String libraryId, String make, String model, AssetOrder order, String originalFileName, String originalPath, num page, List<String> personIds, String resizePath, num size, String state, DateTime takenAfter, DateTime takenBefore, DateTime trashedAfter, DateTime trashedBefore, AssetTypeEnum type, DateTime updatedAfter, DateTime updatedBefore, String webpPath, bool withArchived, bool withDeleted, bool withExif, bool withPeople, bool withStacked }) async
|
||||
test('test searchAssets', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
@@ -36,8 +36,8 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String previewPath
|
||||
test('to test the property `previewPath`', () async {
|
||||
// String resizePath
|
||||
test('to test the property `resizePath`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
+2
-2
@@ -36,8 +36,8 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String previewPath
|
||||
test('to test the property `previewPath`', () async {
|
||||
// String resizePath
|
||||
test('to test the property `resizePath`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
//
|
||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||
//
|
||||
// @dart=2.12
|
||||
|
||||
// ignore_for_file: unused_element, unused_import
|
||||
// ignore_for_file: always_put_required_named_parameters_first
|
||||
// ignore_for_file: constant_identifier_names
|
||||
// ignore_for_file: lines_longer_than_80_chars
|
||||
|
||||
import 'package:openapi/api.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
// tests for ImageFormat
|
||||
void main() {
|
||||
|
||||
group('test ImageFormat', () {
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
+7
-7
@@ -151,8 +151,8 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String previewPath
|
||||
test('to test the property `previewPath`', () async {
|
||||
// String resizePath
|
||||
test('to test the property `resizePath`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
@@ -176,11 +176,6 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String thumbnailPath
|
||||
test('to test the property `thumbnailPath`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// DateTime trashedAfter
|
||||
test('to test the property `trashedAfter`', () async {
|
||||
// TODO
|
||||
@@ -206,6 +201,11 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String webpPath
|
||||
test('to test the property `webpPath`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool withArchived (default value: false)
|
||||
test('to test the property `withArchived`', () async {
|
||||
// TODO
|
||||
|
||||
+5
-5
@@ -21,11 +21,6 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// SystemConfigThumbnailDto image
|
||||
test('to test the property `image`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// SystemConfigJobDto job
|
||||
test('to test the property `job`', () async {
|
||||
// TODO
|
||||
@@ -86,6 +81,11 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// SystemConfigThumbnailDto thumbnail
|
||||
test('to test the property `thumbnail`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// SystemConfigTrashDto trash
|
||||
test('to test the property `trash`', () async {
|
||||
// TODO
|
||||
|
||||
+4
-14
@@ -21,13 +21,8 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// ImageFormat previewFormat
|
||||
test('to test the property `previewFormat`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// int previewSize
|
||||
test('to test the property `previewSize`', () async {
|
||||
// int jpegSize
|
||||
test('to test the property `jpegSize`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
@@ -36,13 +31,8 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// ImageFormat thumbnailFormat
|
||||
test('to test the property `thumbnailFormat`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// int thumbnailSize
|
||||
test('to test the property `thumbnailSize`', () async {
|
||||
// int webpSize
|
||||
test('to test the property `webpSize`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
@@ -2382,7 +2382,7 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "previewPath",
|
||||
"name": "resizePath",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
@@ -2423,14 +2423,6 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "thumbnailPath",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "trashedAfter",
|
||||
"required": false,
|
||||
@@ -2475,6 +2467,14 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "webpPath",
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "withArchived",
|
||||
"required": false,
|
||||
@@ -7776,7 +7776,7 @@
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"previewPath": {
|
||||
"resizePath": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
@@ -7785,7 +7785,7 @@
|
||||
"deviceAssetId",
|
||||
"deviceId",
|
||||
"id",
|
||||
"previewPath"
|
||||
"resizePath"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
@@ -7803,7 +7803,7 @@
|
||||
"object": {
|
||||
"type": "string"
|
||||
},
|
||||
"previewPath": {
|
||||
"resizePath": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
@@ -7812,7 +7812,7 @@
|
||||
"deviceId",
|
||||
"id",
|
||||
"object",
|
||||
"previewPath"
|
||||
"resizePath"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
@@ -8106,13 +8106,6 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"ImageFormat": {
|
||||
"enum": [
|
||||
"jpeg",
|
||||
"webp"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"JobCommand": {
|
||||
"enum": [
|
||||
"start",
|
||||
@@ -8549,7 +8542,7 @@
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"previewPath": {
|
||||
"resizePath": {
|
||||
"type": "string"
|
||||
},
|
||||
"size": {
|
||||
@@ -8566,9 +8559,6 @@
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"thumbnailPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"trashedAfter": {
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
@@ -8588,6 +8578,9 @@
|
||||
"format": "date-time",
|
||||
"type": "string"
|
||||
},
|
||||
"webpPath": {
|
||||
"type": "string"
|
||||
},
|
||||
"withArchived": {
|
||||
"default": false,
|
||||
"type": "boolean"
|
||||
@@ -8740,8 +8733,8 @@
|
||||
"PathType": {
|
||||
"enum": [
|
||||
"original",
|
||||
"preview",
|
||||
"thumbnail",
|
||||
"jpeg_thumbnail",
|
||||
"webp_thumbnail",
|
||||
"encoded_video",
|
||||
"sidecar",
|
||||
"face",
|
||||
@@ -9737,9 +9730,6 @@
|
||||
"ffmpeg": {
|
||||
"$ref": "#/components/schemas/SystemConfigFFmpegDto"
|
||||
},
|
||||
"image": {
|
||||
"$ref": "#/components/schemas/SystemConfigThumbnailDto"
|
||||
},
|
||||
"job": {
|
||||
"$ref": "#/components/schemas/SystemConfigJobDto"
|
||||
},
|
||||
@@ -9776,6 +9766,9 @@
|
||||
"theme": {
|
||||
"$ref": "#/components/schemas/SystemConfigThemeDto"
|
||||
},
|
||||
"thumbnail": {
|
||||
"$ref": "#/components/schemas/SystemConfigThumbnailDto"
|
||||
},
|
||||
"trash": {
|
||||
"$ref": "#/components/schemas/SystemConfigTrashDto"
|
||||
},
|
||||
@@ -9785,7 +9778,6 @@
|
||||
},
|
||||
"required": [
|
||||
"ffmpeg",
|
||||
"image",
|
||||
"job",
|
||||
"library",
|
||||
"logging",
|
||||
@@ -9798,6 +9790,7 @@
|
||||
"server",
|
||||
"storageTemplate",
|
||||
"theme",
|
||||
"thumbnail",
|
||||
"trash",
|
||||
"user"
|
||||
],
|
||||
@@ -10250,29 +10243,21 @@
|
||||
"colorspace": {
|
||||
"$ref": "#/components/schemas/Colorspace"
|
||||
},
|
||||
"previewFormat": {
|
||||
"$ref": "#/components/schemas/ImageFormat"
|
||||
},
|
||||
"previewSize": {
|
||||
"jpegSize": {
|
||||
"type": "integer"
|
||||
},
|
||||
"quality": {
|
||||
"type": "integer"
|
||||
},
|
||||
"thumbnailFormat": {
|
||||
"$ref": "#/components/schemas/ImageFormat"
|
||||
},
|
||||
"thumbnailSize": {
|
||||
"webpSize": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"colorspace",
|
||||
"previewFormat",
|
||||
"previewSize",
|
||||
"jpegSize",
|
||||
"quality",
|
||||
"thumbnailFormat",
|
||||
"thumbnailSize"
|
||||
"webpSize"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
@@ -10335,8 +10320,8 @@
|
||||
},
|
||||
"ThumbnailFormat": {
|
||||
"enum": [
|
||||
"jpeg",
|
||||
"webp"
|
||||
"JPEG",
|
||||
"WEBP"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -242,14 +242,14 @@ export type CuratedLocationsResponseDto = {
|
||||
deviceAssetId: string;
|
||||
deviceId: string;
|
||||
id: string;
|
||||
previewPath: string;
|
||||
resizePath: string;
|
||||
};
|
||||
export type CuratedObjectsResponseDto = {
|
||||
deviceAssetId: string;
|
||||
deviceId: string;
|
||||
id: string;
|
||||
"object": string;
|
||||
previewPath: string;
|
||||
resizePath: string;
|
||||
};
|
||||
export type CheckExistingAssetsDto = {
|
||||
deviceAssetIds: string[];
|
||||
@@ -643,17 +643,17 @@ export type MetadataSearchDto = {
|
||||
originalPath?: string;
|
||||
page?: number;
|
||||
personIds?: string[];
|
||||
previewPath?: string;
|
||||
resizePath?: string;
|
||||
size?: number;
|
||||
state?: string;
|
||||
takenAfter?: string;
|
||||
takenBefore?: string;
|
||||
thumbnailPath?: string;
|
||||
trashedAfter?: string;
|
||||
trashedBefore?: string;
|
||||
"type"?: AssetTypeEnum;
|
||||
updatedAfter?: string;
|
||||
updatedBefore?: string;
|
||||
webpPath?: string;
|
||||
withArchived?: boolean;
|
||||
withDeleted?: boolean;
|
||||
withExif?: boolean;
|
||||
@@ -830,14 +830,6 @@ export type SystemConfigFFmpegDto = {
|
||||
transcode: TranscodePolicy;
|
||||
twoPass: boolean;
|
||||
};
|
||||
export type SystemConfigThumbnailDto = {
|
||||
colorspace: Colorspace;
|
||||
previewFormat: ImageFormat;
|
||||
previewSize: number;
|
||||
quality: number;
|
||||
thumbnailFormat: ImageFormat;
|
||||
thumbnailSize: number;
|
||||
};
|
||||
export type JobSettingsDto = {
|
||||
concurrency: number;
|
||||
};
|
||||
@@ -930,6 +922,12 @@ export type SystemConfigStorageTemplateDto = {
|
||||
export type SystemConfigThemeDto = {
|
||||
customCss: string;
|
||||
};
|
||||
export type SystemConfigThumbnailDto = {
|
||||
colorspace: Colorspace;
|
||||
jpegSize: number;
|
||||
quality: number;
|
||||
webpSize: number;
|
||||
};
|
||||
export type SystemConfigTrashDto = {
|
||||
days: number;
|
||||
enabled: boolean;
|
||||
@@ -939,7 +937,6 @@ export type SystemConfigUserDto = {
|
||||
};
|
||||
export type SystemConfigDto = {
|
||||
ffmpeg: SystemConfigFFmpegDto;
|
||||
image: SystemConfigThumbnailDto;
|
||||
job: SystemConfigJobDto;
|
||||
library: SystemConfigLibraryDto;
|
||||
logging: SystemConfigLoggingDto;
|
||||
@@ -952,6 +949,7 @@ export type SystemConfigDto = {
|
||||
server: SystemConfigServerDto;
|
||||
storageTemplate: SystemConfigStorageTemplateDto;
|
||||
theme: SystemConfigThemeDto;
|
||||
thumbnail: SystemConfigThumbnailDto;
|
||||
trash: SystemConfigTrashDto;
|
||||
user: SystemConfigUserDto;
|
||||
};
|
||||
@@ -1564,7 +1562,7 @@ export function updateAsset({ id, updateAssetDto }: {
|
||||
body: updateAssetDto
|
||||
})));
|
||||
}
|
||||
export function searchAssets({ checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, previewPath, size, state, takenAfter, takenBefore, thumbnailPath, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, withArchived, withDeleted, withExif, withPeople, withStacked }: {
|
||||
export function searchAssets({ checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked }: {
|
||||
checksum?: string;
|
||||
city?: string;
|
||||
country?: string;
|
||||
@@ -1592,17 +1590,17 @@ export function searchAssets({ checksum, city, country, createdAfter, createdBef
|
||||
originalPath?: string;
|
||||
page?: number;
|
||||
personIds?: string[];
|
||||
previewPath?: string;
|
||||
resizePath?: string;
|
||||
size?: number;
|
||||
state?: string;
|
||||
takenAfter?: string;
|
||||
takenBefore?: string;
|
||||
thumbnailPath?: string;
|
||||
trashedAfter?: string;
|
||||
trashedBefore?: string;
|
||||
$type?: AssetTypeEnum;
|
||||
updatedAfter?: string;
|
||||
updatedBefore?: string;
|
||||
webpPath?: string;
|
||||
withArchived?: boolean;
|
||||
withDeleted?: boolean;
|
||||
withExif?: boolean;
|
||||
@@ -1640,17 +1638,17 @@ export function searchAssets({ checksum, city, country, createdAfter, createdBef
|
||||
originalPath,
|
||||
page,
|
||||
personIds,
|
||||
previewPath,
|
||||
resizePath,
|
||||
size,
|
||||
state,
|
||||
takenAfter,
|
||||
takenBefore,
|
||||
thumbnailPath,
|
||||
trashedAfter,
|
||||
trashedBefore,
|
||||
"type": $type,
|
||||
updatedAfter,
|
||||
updatedBefore,
|
||||
webpPath,
|
||||
withArchived,
|
||||
withDeleted,
|
||||
withExif,
|
||||
@@ -2787,8 +2785,8 @@ export enum AssetJobName {
|
||||
TranscodeVideo = "transcode-video"
|
||||
}
|
||||
export enum ThumbnailFormat {
|
||||
Jpeg = "jpeg",
|
||||
Webp = "webp"
|
||||
Jpeg = "JPEG",
|
||||
Webp = "WEBP"
|
||||
}
|
||||
export enum TimeBucketSize {
|
||||
Day = "DAY",
|
||||
@@ -2805,8 +2803,8 @@ export enum PathEntityType {
|
||||
}
|
||||
export enum PathType {
|
||||
Original = "original",
|
||||
Preview = "preview",
|
||||
Thumbnail = "thumbnail",
|
||||
JpegThumbnail = "jpeg_thumbnail",
|
||||
WebpThumbnail = "webp_thumbnail",
|
||||
EncodedVideo = "encoded_video",
|
||||
Sidecar = "sidecar",
|
||||
Face = "face",
|
||||
@@ -2888,14 +2886,6 @@ export enum TranscodePolicy {
|
||||
Required = "required",
|
||||
Disabled = "disabled"
|
||||
}
|
||||
export enum Colorspace {
|
||||
Srgb = "srgb",
|
||||
P3 = "p3"
|
||||
}
|
||||
export enum ImageFormat {
|
||||
Jpeg = "jpeg",
|
||||
Webp = "webp"
|
||||
}
|
||||
export enum LogLevel {
|
||||
Verbose = "verbose",
|
||||
Debug = "debug",
|
||||
@@ -2912,6 +2902,10 @@ export enum ModelType {
|
||||
FacialRecognition = "facial-recognition",
|
||||
Clip = "clip"
|
||||
}
|
||||
export enum Colorspace {
|
||||
Srgb = "srgb",
|
||||
P3 = "p3"
|
||||
}
|
||||
export enum MapTheme {
|
||||
Light = "light",
|
||||
Dark = "dark"
|
||||
|
||||
@@ -46,6 +46,17 @@ WORKDIR /usr/src/app
|
||||
ENV NODE_ENV=production \
|
||||
NVIDIA_DRIVER_CAPABILITIES=all \
|
||||
NVIDIA_VISIBLE_DEVICES=all
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install --no-install-recommends -yqq curl unzip && \
|
||||
curl -fsSL https://bun.sh/install | bash && \
|
||||
apt-get purge -yqq curl unzip && \
|
||||
apt-get autoremove -yqq && \
|
||||
apt-get clean && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
ENV PATH="${PATH}:~/.bun/bin"
|
||||
|
||||
COPY --from=prod /usr/src/app/node_modules ./node_modules
|
||||
COPY --from=prod /usr/src/app/dist ./dist
|
||||
COPY --from=prod /usr/src/app/bin ./bin
|
||||
|
||||
@@ -4,7 +4,6 @@ import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||
import { AssetEntity } from 'src/entities/asset.entity';
|
||||
import { AssetPathType, PathType, PersonPathType } from 'src/entities/move.entity';
|
||||
import { PersonEntity } from 'src/entities/person.entity';
|
||||
import { ImageFormat } from 'src/entities/system-config.entity';
|
||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
||||
import { IMoveRepository } from 'src/interfaces/move.interface';
|
||||
@@ -35,8 +34,7 @@ export interface MoveRequest {
|
||||
};
|
||||
}
|
||||
|
||||
export type GeneratedImageType = AssetPathType.PREVIEW | AssetPathType.THUMBNAIL;
|
||||
export type GeneratedAssetType = GeneratedImageType | AssetPathType.ENCODED_VIDEO;
|
||||
type GeneratedAssetPath = AssetPathType.JPEG_THUMBNAIL | AssetPathType.WEBP_THUMBNAIL | AssetPathType.ENCODED_VIDEO;
|
||||
|
||||
let instance: StorageCore | null;
|
||||
|
||||
@@ -96,8 +94,12 @@ export class StorageCore {
|
||||
return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, person.ownerId, `${person.id}.jpeg`);
|
||||
}
|
||||
|
||||
static getImagePath(asset: AssetEntity, type: GeneratedImageType, format: ImageFormat) {
|
||||
return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, asset.ownerId, `${asset.id}-${type}.${format}`);
|
||||
static getLargeThumbnailPath(asset: AssetEntity) {
|
||||
return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, asset.ownerId, `${asset.id}.jpeg`);
|
||||
}
|
||||
|
||||
static getSmallThumbnailPath(asset: AssetEntity) {
|
||||
return StorageCore.getNestedPath(StorageFolder.THUMBNAILS, asset.ownerId, `${asset.id}.webp`);
|
||||
}
|
||||
|
||||
static getEncodedVideoPath(asset: AssetEntity) {
|
||||
@@ -120,23 +122,34 @@ export class StorageCore {
|
||||
return path.startsWith(THUMBNAIL_DIR) || path.startsWith(ENCODED_VIDEO_DIR);
|
||||
}
|
||||
|
||||
async moveAssetImage(asset: AssetEntity, pathType: GeneratedImageType, format: ImageFormat) {
|
||||
const { id: entityId, previewPath, thumbnailPath } = asset;
|
||||
return this.moveFile({
|
||||
entityId,
|
||||
pathType,
|
||||
oldPath: pathType === AssetPathType.PREVIEW ? previewPath : thumbnailPath,
|
||||
newPath: StorageCore.getImagePath(asset, AssetPathType.THUMBNAIL, format),
|
||||
});
|
||||
}
|
||||
|
||||
async moveAssetVideo(asset: AssetEntity) {
|
||||
return this.moveFile({
|
||||
entityId: asset.id,
|
||||
pathType: AssetPathType.ENCODED_VIDEO,
|
||||
oldPath: asset.encodedVideoPath,
|
||||
newPath: StorageCore.getEncodedVideoPath(asset),
|
||||
});
|
||||
async moveAssetFile(asset: AssetEntity, pathType: GeneratedAssetPath) {
|
||||
const { id: entityId, resizePath, webpPath, encodedVideoPath } = asset;
|
||||
switch (pathType) {
|
||||
case AssetPathType.JPEG_THUMBNAIL: {
|
||||
return this.moveFile({
|
||||
entityId,
|
||||
pathType,
|
||||
oldPath: resizePath,
|
||||
newPath: StorageCore.getLargeThumbnailPath(asset),
|
||||
});
|
||||
}
|
||||
case AssetPathType.WEBP_THUMBNAIL: {
|
||||
return this.moveFile({
|
||||
entityId,
|
||||
pathType,
|
||||
oldPath: webpPath,
|
||||
newPath: StorageCore.getSmallThumbnailPath(asset),
|
||||
});
|
||||
}
|
||||
case AssetPathType.ENCODED_VIDEO: {
|
||||
return this.moveFile({
|
||||
entityId,
|
||||
pathType,
|
||||
oldPath: encodedVideoPath,
|
||||
newPath: StorageCore.getEncodedVideoPath(asset),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async movePersonFile(person: PersonEntity, pathType: PersonPathType) {
|
||||
@@ -275,11 +288,11 @@ export class StorageCore {
|
||||
case AssetPathType.ORIGINAL: {
|
||||
return this.assetRepository.update({ id, originalPath: newPath });
|
||||
}
|
||||
case AssetPathType.PREVIEW: {
|
||||
return this.assetRepository.update({ id, previewPath: newPath });
|
||||
case AssetPathType.JPEG_THUMBNAIL: {
|
||||
return this.assetRepository.update({ id, resizePath: newPath });
|
||||
}
|
||||
case AssetPathType.THUMBNAIL: {
|
||||
return this.assetRepository.update({ id, thumbnailPath: newPath });
|
||||
case AssetPathType.WEBP_THUMBNAIL: {
|
||||
return this.assetRepository.update({ id, webpPath: newPath });
|
||||
}
|
||||
case AssetPathType.ENCODED_VIDEO: {
|
||||
return this.assetRepository.update({ id, encodedVideoPath: newPath });
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
AudioCodec,
|
||||
CQMode,
|
||||
Colorspace,
|
||||
ImageFormat,
|
||||
LogLevel,
|
||||
SystemConfig,
|
||||
SystemConfigEntity,
|
||||
@@ -113,11 +112,9 @@ export const defaults = Object.freeze<SystemConfig>({
|
||||
hashVerificationEnabled: true,
|
||||
template: '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}',
|
||||
},
|
||||
image: {
|
||||
thumbnailFormat: ImageFormat.WEBP,
|
||||
thumbnailSize: 250,
|
||||
previewFormat: ImageFormat.JPEG,
|
||||
previewSize: 1440,
|
||||
thumbnail: {
|
||||
webpSize: 250,
|
||||
jpegSize: 1440,
|
||||
quality: 80,
|
||||
colorspace: Colorspace.P3,
|
||||
},
|
||||
|
||||
@@ -82,7 +82,7 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As
|
||||
type: entity.type,
|
||||
thumbhash: entity.thumbhash?.toString('base64') ?? null,
|
||||
localDateTime: entity.localDateTime,
|
||||
resized: !!entity.previewPath,
|
||||
resized: !!entity.resizePath,
|
||||
duration: entity.duration ?? '0:00:00.00000',
|
||||
livePhotoVideoId: entity.livePhotoVideoId,
|
||||
hasMetadata: false,
|
||||
@@ -100,7 +100,7 @@ export function mapAsset(entity: AssetEntity, options: AssetMapOptions = {}): As
|
||||
type: entity.type,
|
||||
originalPath: entity.originalPath,
|
||||
originalFileName: entity.originalFileName,
|
||||
resized: !!entity.previewPath,
|
||||
resized: !!entity.resizePath,
|
||||
thumbhash: entity.thumbhash?.toString('base64') ?? null,
|
||||
fileCreatedAt: entity.fileCreatedAt,
|
||||
fileModifiedAt: entity.fileModifiedAt,
|
||||
|
||||
@@ -31,7 +31,7 @@ export class CheckExistingAssetsResponseDto {
|
||||
export class CuratedLocationsResponseDto {
|
||||
id!: string;
|
||||
city!: string;
|
||||
previewPath!: string;
|
||||
resizePath!: string;
|
||||
deviceAssetId!: string;
|
||||
deviceId!: string;
|
||||
}
|
||||
@@ -39,7 +39,7 @@ export class CuratedLocationsResponseDto {
|
||||
export class CuratedObjectsResponseDto {
|
||||
id!: string;
|
||||
object!: string;
|
||||
previewPath!: string;
|
||||
resizePath!: string;
|
||||
deviceAssetId!: string;
|
||||
deviceId!: string;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { ArrayNotEmpty, IsArray, IsEnum, IsInt, IsNotEmpty, IsString, IsUUID, ValidateNested } from 'class-validator';
|
||||
import { UploadFieldName } from 'src/dtos/asset.dto';
|
||||
import { ImageFormat } from 'src/entities/system-config.entity';
|
||||
import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from 'src/validation';
|
||||
|
||||
export class AssetBulkUploadCheckItem {
|
||||
@@ -113,17 +112,22 @@ export class CreateAssetDto {
|
||||
[UploadFieldName.SIDECAR_DATA]?: any;
|
||||
}
|
||||
|
||||
export enum GetAssetThumbnailFormatEnum {
|
||||
JPEG = 'JPEG',
|
||||
WEBP = 'WEBP',
|
||||
}
|
||||
|
||||
export class GetAssetThumbnailDto {
|
||||
@Optional()
|
||||
@IsEnum(ImageFormat)
|
||||
@IsEnum(GetAssetThumbnailFormatEnum)
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
enum: ImageFormat,
|
||||
default: ImageFormat.WEBP,
|
||||
enum: GetAssetThumbnailFormatEnum,
|
||||
default: GetAssetThumbnailFormatEnum.WEBP,
|
||||
required: false,
|
||||
enumName: 'ThumbnailFormat',
|
||||
})
|
||||
format: ImageFormat = ImageFormat.WEBP;
|
||||
format: GetAssetThumbnailFormatEnum = GetAssetThumbnailFormatEnum.WEBP;
|
||||
}
|
||||
|
||||
export class SearchPropertiesDto {
|
||||
|
||||
@@ -163,12 +163,12 @@ export class MetadataSearchDto extends BaseSearchDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Optional()
|
||||
previewPath?: string;
|
||||
resizePath?: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Optional()
|
||||
thumbnailPath?: string;
|
||||
webpPath?: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
|
||||
@@ -22,7 +22,6 @@ import {
|
||||
AudioCodec,
|
||||
CQMode,
|
||||
Colorspace,
|
||||
ImageFormat,
|
||||
LogLevel,
|
||||
SystemConfig,
|
||||
ToneMapping,
|
||||
@@ -387,25 +386,17 @@ export class SystemConfigThemeDto {
|
||||
}
|
||||
|
||||
class SystemConfigThumbnailDto {
|
||||
@IsEnum(ImageFormat)
|
||||
@ApiProperty({ enumName: 'ImageFormat', enum: ImageFormat })
|
||||
thumbnailFormat!: ImageFormat;
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Type(() => Number)
|
||||
@ApiProperty({ type: 'integer' })
|
||||
webpSize!: number;
|
||||
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Type(() => Number)
|
||||
@ApiProperty({ type: 'integer' })
|
||||
thumbnailSize!: number;
|
||||
|
||||
@IsEnum(ImageFormat)
|
||||
@ApiProperty({ enumName: 'ImageFormat', enum: ImageFormat })
|
||||
previewFormat!: ImageFormat;
|
||||
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Type(() => Number)
|
||||
@ApiProperty({ type: 'integer' })
|
||||
previewSize!: number;
|
||||
jpegSize!: number;
|
||||
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@@ -492,7 +483,7 @@ export class SystemConfigDto implements SystemConfig {
|
||||
@Type(() => SystemConfigThumbnailDto)
|
||||
@ValidateNested()
|
||||
@IsObject()
|
||||
image!: SystemConfigThumbnailDto;
|
||||
thumbnail!: SystemConfigThumbnailDto;
|
||||
|
||||
@Type(() => SystemConfigTrashDto)
|
||||
@ValidateNested()
|
||||
|
||||
@@ -67,10 +67,10 @@ export class AssetEntity {
|
||||
originalPath!: string;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
previewPath!: string | null;
|
||||
resizePath!: string | null;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true, default: '' })
|
||||
thumbnailPath!: string | null;
|
||||
webpPath!: string | null;
|
||||
|
||||
@Column({ type: 'bytea', nullable: true })
|
||||
thumbhash!: Buffer | null;
|
||||
|
||||
@@ -24,8 +24,8 @@ export class MoveEntity {
|
||||
|
||||
export enum AssetPathType {
|
||||
ORIGINAL = 'original',
|
||||
PREVIEW = 'preview',
|
||||
THUMBNAIL = 'thumbnail',
|
||||
JPEG_THUMBNAIL = 'jpeg_thumbnail',
|
||||
WEBP_THUMBNAIL = 'webp_thumbnail',
|
||||
ENCODED_VIDEO = 'encoded_video',
|
||||
SIDECAR = 'sidecar',
|
||||
}
|
||||
|
||||
@@ -165,11 +165,6 @@ export enum Colorspace {
|
||||
P3 = 'p3',
|
||||
}
|
||||
|
||||
export enum ImageFormat {
|
||||
JPEG = 'jpeg',
|
||||
WEBP = 'webp',
|
||||
}
|
||||
|
||||
export enum LogLevel {
|
||||
VERBOSE = 'verbose',
|
||||
DEBUG = 'debug',
|
||||
@@ -254,11 +249,9 @@ export interface SystemConfig {
|
||||
hashVerificationEnabled: boolean;
|
||||
template: string;
|
||||
};
|
||||
image: {
|
||||
thumbnailFormat: ImageFormat;
|
||||
thumbnailSize: number;
|
||||
previewFormat: ImageFormat;
|
||||
previewSize: number;
|
||||
thumbnail: {
|
||||
webpSize: number;
|
||||
jpegSize: number;
|
||||
quality: number;
|
||||
colorspace: Colorspace;
|
||||
};
|
||||
|
||||
@@ -33,8 +33,8 @@ export enum JobName {
|
||||
|
||||
// thumbnails
|
||||
QUEUE_GENERATE_THUMBNAILS = 'queue-generate-thumbnails',
|
||||
GENERATE_THUMBNAIL = 'generate-thumbnail',
|
||||
GENERATE_PREVIEW = 'generate-preview',
|
||||
GENERATE_JPEG_THUMBNAIL = 'generate-jpeg-thumbnail',
|
||||
GENERATE_WEBP_THUMBNAIL = 'generate-webp-thumbnail',
|
||||
GENERATE_THUMBHASH_THUMBNAIL = 'generate-thumbhash-thumbnail',
|
||||
GENERATE_PERSON_THUMBNAIL = 'generate-person-thumbnail',
|
||||
|
||||
@@ -160,8 +160,8 @@ export type JobItem =
|
||||
|
||||
// Thumbnails
|
||||
| { name: JobName.QUEUE_GENERATE_THUMBNAILS; data: IBaseJob }
|
||||
| { name: JobName.GENERATE_THUMBNAIL; data: IEntityJob }
|
||||
| { name: JobName.GENERATE_PREVIEW; data: IEntityJob }
|
||||
| { name: JobName.GENERATE_JPEG_THUMBNAIL; data: IEntityJob }
|
||||
| { name: JobName.GENERATE_WEBP_THUMBNAIL; data: IEntityJob }
|
||||
| { name: JobName.GENERATE_THUMBHASH_THUMBNAIL; data: IEntityJob }
|
||||
|
||||
// User
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Writable } from 'node:stream';
|
||||
import { ImageFormat, TranscodeTarget, VideoCodec } from 'src/entities/system-config.entity';
|
||||
import { TranscodeTarget, VideoCodec } from 'src/entities/system-config.entity';
|
||||
|
||||
export const IMediaRepository = 'IMediaRepository';
|
||||
|
||||
export interface ResizeOptions {
|
||||
size: number;
|
||||
format: ImageFormat;
|
||||
format: 'webp' | 'jpeg';
|
||||
colorspace: string;
|
||||
quality: number;
|
||||
}
|
||||
|
||||
@@ -117,8 +117,8 @@ export interface SearchPathOptions {
|
||||
encodedVideoPath?: string;
|
||||
originalFileName?: string;
|
||||
originalPath?: string;
|
||||
previewPath?: string;
|
||||
thumbnailPath?: string;
|
||||
resizePath?: string;
|
||||
webpPath?: string;
|
||||
}
|
||||
|
||||
export interface SearchExifOptions {
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class RenameWebpJpegPaths1711257900274 implements MigrationInterface {
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.renameColumn('assets', 'webpPath', 'thumbnailPath');
|
||||
await queryRunner.renameColumn('assets', 'resizePath', 'previewPath');
|
||||
await queryRunner.query(`
|
||||
UPDATE system_config
|
||||
SET key = 'image.previewSize'
|
||||
WHERE key = 'thumbnail.jpegSize'`);
|
||||
await queryRunner.query(
|
||||
`UPDATE system_config
|
||||
SET key = 'image.thumbnailSize'
|
||||
WHERE key = 'thumbnail.webpSize'`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.renameColumn('assets', 'thumbnailPath', 'webpPath');
|
||||
await queryRunner.renameColumn('assets', 'previewPath', 'resizePath');
|
||||
await queryRunner.query(`
|
||||
UPDATE system_config
|
||||
SET key = 'thumbnail.jpegSize'
|
||||
WHERE key = 'image.previewSize'`);
|
||||
await queryRunner.query(
|
||||
`UPDATE system_config
|
||||
SET key = 'thumbnail.webpSize'
|
||||
WHERE key = 'image.thumbnailSize'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,8 @@ SELECT
|
||||
"AssetEntity"."deviceId" AS "AssetEntity_deviceId",
|
||||
"AssetEntity"."type" AS "AssetEntity_type",
|
||||
"AssetEntity"."originalPath" AS "AssetEntity_originalPath",
|
||||
"AssetEntity"."previewPath" AS "AssetEntity_previewPath",
|
||||
"AssetEntity"."thumbnailPath" AS "AssetEntity_thumbnailPath",
|
||||
"AssetEntity"."resizePath" AS "AssetEntity_resizePath",
|
||||
"AssetEntity"."webpPath" AS "AssetEntity_webpPath",
|
||||
"AssetEntity"."thumbhash" AS "AssetEntity_thumbhash",
|
||||
"AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath",
|
||||
"AssetEntity"."createdAt" AS "AssetEntity_createdAt",
|
||||
@@ -45,8 +45,8 @@ SELECT
|
||||
"AssetEntity"."deviceId" AS "AssetEntity_deviceId",
|
||||
"AssetEntity"."type" AS "AssetEntity_type",
|
||||
"AssetEntity"."originalPath" AS "AssetEntity_originalPath",
|
||||
"AssetEntity"."previewPath" AS "AssetEntity_previewPath",
|
||||
"AssetEntity"."thumbnailPath" AS "AssetEntity_thumbnailPath",
|
||||
"AssetEntity"."resizePath" AS "AssetEntity_resizePath",
|
||||
"AssetEntity"."webpPath" AS "AssetEntity_webpPath",
|
||||
"AssetEntity"."thumbhash" AS "AssetEntity_thumbhash",
|
||||
"AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath",
|
||||
"AssetEntity"."createdAt" AS "AssetEntity_createdAt",
|
||||
@@ -130,8 +130,8 @@ SELECT
|
||||
"bd93d5747511a4dad4923546c51365bf1a803774"."deviceId" AS "bd93d5747511a4dad4923546c51365bf1a803774_deviceId",
|
||||
"bd93d5747511a4dad4923546c51365bf1a803774"."type" AS "bd93d5747511a4dad4923546c51365bf1a803774_type",
|
||||
"bd93d5747511a4dad4923546c51365bf1a803774"."originalPath" AS "bd93d5747511a4dad4923546c51365bf1a803774_originalPath",
|
||||
"bd93d5747511a4dad4923546c51365bf1a803774"."previewPath" AS "bd93d5747511a4dad4923546c51365bf1a803774_previewPath",
|
||||
"bd93d5747511a4dad4923546c51365bf1a803774"."thumbnailPath" AS "bd93d5747511a4dad4923546c51365bf1a803774_thumbnailPath",
|
||||
"bd93d5747511a4dad4923546c51365bf1a803774"."resizePath" AS "bd93d5747511a4dad4923546c51365bf1a803774_resizePath",
|
||||
"bd93d5747511a4dad4923546c51365bf1a803774"."webpPath" AS "bd93d5747511a4dad4923546c51365bf1a803774_webpPath",
|
||||
"bd93d5747511a4dad4923546c51365bf1a803774"."thumbhash" AS "bd93d5747511a4dad4923546c51365bf1a803774_thumbhash",
|
||||
"bd93d5747511a4dad4923546c51365bf1a803774"."encodedVideoPath" AS "bd93d5747511a4dad4923546c51365bf1a803774_encodedVideoPath",
|
||||
"bd93d5747511a4dad4923546c51365bf1a803774"."createdAt" AS "bd93d5747511a4dad4923546c51365bf1a803774_createdAt",
|
||||
@@ -183,8 +183,8 @@ FROM
|
||||
"AssetEntity"."deviceId" AS "AssetEntity_deviceId",
|
||||
"AssetEntity"."type" AS "AssetEntity_type",
|
||||
"AssetEntity"."originalPath" AS "AssetEntity_originalPath",
|
||||
"AssetEntity"."previewPath" AS "AssetEntity_previewPath",
|
||||
"AssetEntity"."thumbnailPath" AS "AssetEntity_thumbnailPath",
|
||||
"AssetEntity"."resizePath" AS "AssetEntity_resizePath",
|
||||
"AssetEntity"."webpPath" AS "AssetEntity_webpPath",
|
||||
"AssetEntity"."thumbhash" AS "AssetEntity_thumbhash",
|
||||
"AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath",
|
||||
"AssetEntity"."createdAt" AS "AssetEntity_createdAt",
|
||||
@@ -280,8 +280,8 @@ SELECT
|
||||
"AssetEntity"."deviceId" AS "AssetEntity_deviceId",
|
||||
"AssetEntity"."type" AS "AssetEntity_type",
|
||||
"AssetEntity"."originalPath" AS "AssetEntity_originalPath",
|
||||
"AssetEntity"."previewPath" AS "AssetEntity_previewPath",
|
||||
"AssetEntity"."thumbnailPath" AS "AssetEntity_thumbnailPath",
|
||||
"AssetEntity"."resizePath" AS "AssetEntity_resizePath",
|
||||
"AssetEntity"."webpPath" AS "AssetEntity_webpPath",
|
||||
"AssetEntity"."thumbhash" AS "AssetEntity_thumbhash",
|
||||
"AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath",
|
||||
"AssetEntity"."createdAt" AS "AssetEntity_createdAt",
|
||||
@@ -326,8 +326,8 @@ SELECT
|
||||
"AssetEntity"."deviceId" AS "AssetEntity_deviceId",
|
||||
"AssetEntity"."type" AS "AssetEntity_type",
|
||||
"AssetEntity"."originalPath" AS "AssetEntity_originalPath",
|
||||
"AssetEntity"."previewPath" AS "AssetEntity_previewPath",
|
||||
"AssetEntity"."thumbnailPath" AS "AssetEntity_thumbnailPath",
|
||||
"AssetEntity"."resizePath" AS "AssetEntity_resizePath",
|
||||
"AssetEntity"."webpPath" AS "AssetEntity_webpPath",
|
||||
"AssetEntity"."thumbhash" AS "AssetEntity_thumbhash",
|
||||
"AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath",
|
||||
"AssetEntity"."createdAt" AS "AssetEntity_createdAt",
|
||||
@@ -370,8 +370,8 @@ SELECT
|
||||
"AssetEntity"."deviceId" AS "AssetEntity_deviceId",
|
||||
"AssetEntity"."type" AS "AssetEntity_type",
|
||||
"AssetEntity"."originalPath" AS "AssetEntity_originalPath",
|
||||
"AssetEntity"."previewPath" AS "AssetEntity_previewPath",
|
||||
"AssetEntity"."thumbnailPath" AS "AssetEntity_thumbnailPath",
|
||||
"AssetEntity"."resizePath" AS "AssetEntity_resizePath",
|
||||
"AssetEntity"."webpPath" AS "AssetEntity_webpPath",
|
||||
"AssetEntity"."thumbhash" AS "AssetEntity_thumbhash",
|
||||
"AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath",
|
||||
"AssetEntity"."createdAt" AS "AssetEntity_createdAt",
|
||||
@@ -459,8 +459,8 @@ SELECT
|
||||
"asset"."deviceId" AS "asset_deviceId",
|
||||
"asset"."type" AS "asset_type",
|
||||
"asset"."originalPath" AS "asset_originalPath",
|
||||
"asset"."previewPath" AS "asset_previewPath",
|
||||
"asset"."thumbnailPath" AS "asset_thumbnailPath",
|
||||
"asset"."resizePath" AS "asset_resizePath",
|
||||
"asset"."webpPath" AS "asset_webpPath",
|
||||
"asset"."thumbhash" AS "asset_thumbhash",
|
||||
"asset"."encodedVideoPath" AS "asset_encodedVideoPath",
|
||||
"asset"."createdAt" AS "asset_createdAt",
|
||||
@@ -518,8 +518,8 @@ SELECT
|
||||
"stackedAssets"."deviceId" AS "stackedAssets_deviceId",
|
||||
"stackedAssets"."type" AS "stackedAssets_type",
|
||||
"stackedAssets"."originalPath" AS "stackedAssets_originalPath",
|
||||
"stackedAssets"."previewPath" AS "stackedAssets_previewPath",
|
||||
"stackedAssets"."thumbnailPath" AS "stackedAssets_thumbnailPath",
|
||||
"stackedAssets"."resizePath" AS "stackedAssets_resizePath",
|
||||
"stackedAssets"."webpPath" AS "stackedAssets_webpPath",
|
||||
"stackedAssets"."thumbhash" AS "stackedAssets_thumbhash",
|
||||
"stackedAssets"."encodedVideoPath" AS "stackedAssets_encodedVideoPath",
|
||||
"stackedAssets"."createdAt" AS "stackedAssets_createdAt",
|
||||
|
||||
@@ -152,8 +152,8 @@ FROM
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."deviceId" AS "AssetFaceEntity__AssetFaceEntity_asset_deviceId",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."type" AS "AssetFaceEntity__AssetFaceEntity_asset_type",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."originalPath" AS "AssetFaceEntity__AssetFaceEntity_asset_originalPath",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."previewPath" AS "AssetFaceEntity__AssetFaceEntity_asset_previewPath",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."thumbnailPath" AS "AssetFaceEntity__AssetFaceEntity_asset_thumbnailPath",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."resizePath" AS "AssetFaceEntity__AssetFaceEntity_asset_resizePath",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."webpPath" AS "AssetFaceEntity__AssetFaceEntity_asset_webpPath",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."thumbhash" AS "AssetFaceEntity__AssetFaceEntity_asset_thumbhash",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."encodedVideoPath" AS "AssetFaceEntity__AssetFaceEntity_asset_encodedVideoPath",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."createdAt" AS "AssetFaceEntity__AssetFaceEntity_asset_createdAt",
|
||||
@@ -250,8 +250,8 @@ FROM
|
||||
"AssetEntity"."deviceId" AS "AssetEntity_deviceId",
|
||||
"AssetEntity"."type" AS "AssetEntity_type",
|
||||
"AssetEntity"."originalPath" AS "AssetEntity_originalPath",
|
||||
"AssetEntity"."previewPath" AS "AssetEntity_previewPath",
|
||||
"AssetEntity"."thumbnailPath" AS "AssetEntity_thumbnailPath",
|
||||
"AssetEntity"."resizePath" AS "AssetEntity_resizePath",
|
||||
"AssetEntity"."webpPath" AS "AssetEntity_webpPath",
|
||||
"AssetEntity"."thumbhash" AS "AssetEntity_thumbhash",
|
||||
"AssetEntity"."encodedVideoPath" AS "AssetEntity_encodedVideoPath",
|
||||
"AssetEntity"."createdAt" AS "AssetEntity_createdAt",
|
||||
@@ -380,8 +380,8 @@ SELECT
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."deviceId" AS "AssetFaceEntity__AssetFaceEntity_asset_deviceId",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."type" AS "AssetFaceEntity__AssetFaceEntity_asset_type",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."originalPath" AS "AssetFaceEntity__AssetFaceEntity_asset_originalPath",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."previewPath" AS "AssetFaceEntity__AssetFaceEntity_asset_previewPath",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."thumbnailPath" AS "AssetFaceEntity__AssetFaceEntity_asset_thumbnailPath",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."resizePath" AS "AssetFaceEntity__AssetFaceEntity_asset_resizePath",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."webpPath" AS "AssetFaceEntity__AssetFaceEntity_asset_webpPath",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."thumbhash" AS "AssetFaceEntity__AssetFaceEntity_asset_thumbhash",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."encodedVideoPath" AS "AssetFaceEntity__AssetFaceEntity_asset_encodedVideoPath",
|
||||
"AssetFaceEntity__AssetFaceEntity_asset"."createdAt" AS "AssetFaceEntity__AssetFaceEntity_asset_createdAt",
|
||||
|
||||
@@ -14,8 +14,8 @@ FROM
|
||||
"asset"."deviceId" AS "asset_deviceId",
|
||||
"asset"."type" AS "asset_type",
|
||||
"asset"."originalPath" AS "asset_originalPath",
|
||||
"asset"."previewPath" AS "asset_previewPath",
|
||||
"asset"."thumbnailPath" AS "asset_thumbnailPath",
|
||||
"asset"."resizePath" AS "asset_resizePath",
|
||||
"asset"."webpPath" AS "asset_webpPath",
|
||||
"asset"."thumbhash" AS "asset_thumbhash",
|
||||
"asset"."encodedVideoPath" AS "asset_encodedVideoPath",
|
||||
"asset"."createdAt" AS "asset_createdAt",
|
||||
@@ -45,8 +45,8 @@ FROM
|
||||
"stackedAssets"."deviceId" AS "stackedAssets_deviceId",
|
||||
"stackedAssets"."type" AS "stackedAssets_type",
|
||||
"stackedAssets"."originalPath" AS "stackedAssets_originalPath",
|
||||
"stackedAssets"."previewPath" AS "stackedAssets_previewPath",
|
||||
"stackedAssets"."thumbnailPath" AS "stackedAssets_thumbnailPath",
|
||||
"stackedAssets"."resizePath" AS "stackedAssets_resizePath",
|
||||
"stackedAssets"."webpPath" AS "stackedAssets_webpPath",
|
||||
"stackedAssets"."thumbhash" AS "stackedAssets_thumbhash",
|
||||
"stackedAssets"."encodedVideoPath" AS "stackedAssets_encodedVideoPath",
|
||||
"stackedAssets"."createdAt" AS "stackedAssets_createdAt",
|
||||
@@ -110,8 +110,8 @@ SELECT
|
||||
"asset"."deviceId" AS "asset_deviceId",
|
||||
"asset"."type" AS "asset_type",
|
||||
"asset"."originalPath" AS "asset_originalPath",
|
||||
"asset"."previewPath" AS "asset_previewPath",
|
||||
"asset"."thumbnailPath" AS "asset_thumbnailPath",
|
||||
"asset"."resizePath" AS "asset_resizePath",
|
||||
"asset"."webpPath" AS "asset_webpPath",
|
||||
"asset"."thumbhash" AS "asset_thumbhash",
|
||||
"asset"."encodedVideoPath" AS "asset_encodedVideoPath",
|
||||
"asset"."createdAt" AS "asset_createdAt",
|
||||
@@ -141,8 +141,8 @@ SELECT
|
||||
"stackedAssets"."deviceId" AS "stackedAssets_deviceId",
|
||||
"stackedAssets"."type" AS "stackedAssets_type",
|
||||
"stackedAssets"."originalPath" AS "stackedAssets_originalPath",
|
||||
"stackedAssets"."previewPath" AS "stackedAssets_previewPath",
|
||||
"stackedAssets"."thumbnailPath" AS "stackedAssets_thumbnailPath",
|
||||
"stackedAssets"."resizePath" AS "stackedAssets_resizePath",
|
||||
"stackedAssets"."webpPath" AS "stackedAssets_webpPath",
|
||||
"stackedAssets"."thumbhash" AS "stackedAssets_thumbhash",
|
||||
"stackedAssets"."encodedVideoPath" AS "stackedAssets_encodedVideoPath",
|
||||
"stackedAssets"."createdAt" AS "stackedAssets_createdAt",
|
||||
@@ -320,8 +320,8 @@ SELECT
|
||||
"asset"."deviceId" AS "asset_deviceId",
|
||||
"asset"."type" AS "asset_type",
|
||||
"asset"."originalPath" AS "asset_originalPath",
|
||||
"asset"."previewPath" AS "asset_previewPath",
|
||||
"asset"."thumbnailPath" AS "asset_thumbnailPath",
|
||||
"asset"."resizePath" AS "asset_resizePath",
|
||||
"asset"."webpPath" AS "asset_webpPath",
|
||||
"asset"."thumbhash" AS "asset_thumbhash",
|
||||
"asset"."encodedVideoPath" AS "asset_encodedVideoPath",
|
||||
"asset"."createdAt" AS "asset_createdAt",
|
||||
|
||||
@@ -28,8 +28,8 @@ FROM
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."deviceId" AS "SharedLinkEntity__SharedLinkEntity_assets_deviceId",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."type" AS "SharedLinkEntity__SharedLinkEntity_assets_type",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."originalPath" AS "SharedLinkEntity__SharedLinkEntity_assets_originalPath",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."previewPath" AS "SharedLinkEntity__SharedLinkEntity_assets_previewPath",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."thumbnailPath" AS "SharedLinkEntity__SharedLinkEntity_assets_thumbnailPath",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."resizePath" AS "SharedLinkEntity__SharedLinkEntity_assets_resizePath",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."webpPath" AS "SharedLinkEntity__SharedLinkEntity_assets_webpPath",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."thumbhash" AS "SharedLinkEntity__SharedLinkEntity_assets_thumbhash",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."encodedVideoPath" AS "SharedLinkEntity__SharedLinkEntity_assets_encodedVideoPath",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."createdAt" AS "SharedLinkEntity__SharedLinkEntity_assets_createdAt",
|
||||
@@ -95,8 +95,8 @@ FROM
|
||||
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."deviceId" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_deviceId",
|
||||
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."type" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_type",
|
||||
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."originalPath" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_originalPath",
|
||||
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."previewPath" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_previewPath",
|
||||
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."thumbnailPath" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_thumbnailPath",
|
||||
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."resizePath" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_resizePath",
|
||||
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."webpPath" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_webpPath",
|
||||
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."thumbhash" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_thumbhash",
|
||||
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."encodedVideoPath" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_encodedVideoPath",
|
||||
"4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6"."createdAt" AS "4a35f463ae8c5544ede95c4b6d9ce8c686b6bfe6_createdAt",
|
||||
@@ -218,8 +218,8 @@ SELECT
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."deviceId" AS "SharedLinkEntity__SharedLinkEntity_assets_deviceId",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."type" AS "SharedLinkEntity__SharedLinkEntity_assets_type",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."originalPath" AS "SharedLinkEntity__SharedLinkEntity_assets_originalPath",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."previewPath" AS "SharedLinkEntity__SharedLinkEntity_assets_previewPath",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."thumbnailPath" AS "SharedLinkEntity__SharedLinkEntity_assets_thumbnailPath",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."resizePath" AS "SharedLinkEntity__SharedLinkEntity_assets_resizePath",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."webpPath" AS "SharedLinkEntity__SharedLinkEntity_assets_webpPath",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."thumbhash" AS "SharedLinkEntity__SharedLinkEntity_assets_thumbhash",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."encodedVideoPath" AS "SharedLinkEntity__SharedLinkEntity_assets_encodedVideoPath",
|
||||
"SharedLinkEntity__SharedLinkEntity_assets"."createdAt" AS "SharedLinkEntity__SharedLinkEntity_assets_createdAt",
|
||||
|
||||
@@ -66,7 +66,7 @@ export class AssetRepositoryV1 implements IAssetRepositoryV1 {
|
||||
getDetectedObjectsByUserId(userId: string): Promise<CuratedObjectsResponseDto[]> {
|
||||
return this.assetRepository.query(
|
||||
`
|
||||
SELECT DISTINCT ON (unnest(si.objects)) a.id, unnest(si.objects) as "object", a."previewPath", a."deviceAssetId", a."deviceId"
|
||||
SELECT DISTINCT ON (unnest(si.objects)) a.id, unnest(si.objects) as "object", a."resizePath", a."deviceAssetId", a."deviceId"
|
||||
FROM assets a
|
||||
LEFT JOIN smart_info si ON a.id = si."assetId"
|
||||
WHERE a."ownerId" = $1
|
||||
@@ -80,7 +80,7 @@ export class AssetRepositoryV1 implements IAssetRepositoryV1 {
|
||||
getLocationsByUserId(userId: string): Promise<CuratedLocationsResponseDto[]> {
|
||||
return this.assetRepository.query(
|
||||
`
|
||||
SELECT DISTINCT ON (e.city) a.id, e.city, a."previewPath", a."deviceAssetId", a."deviceId"
|
||||
SELECT DISTINCT ON (e.city) a.id, e.city, a."resizePath", a."deviceAssetId", a."deviceId"
|
||||
FROM assets a
|
||||
LEFT JOIN exif e ON a.id = e."assetId"
|
||||
WHERE a."ownerId" = $1
|
||||
|
||||
@@ -83,7 +83,7 @@ export class AssetRepository implements IAssetRepository {
|
||||
`entity.ownerId IN (:...ownerIds)
|
||||
AND entity.isVisible = true
|
||||
AND entity.isArchived = false
|
||||
AND entity.previewPath IS NOT NULL
|
||||
AND entity.resizePath IS NOT NULL
|
||||
AND EXTRACT(DAY FROM entity.localDateTime AT TIME ZONE 'UTC') = :day
|
||||
AND EXTRACT(MONTH FROM entity.localDateTime AT TIME ZONE 'UTC') = :month`,
|
||||
{
|
||||
@@ -302,10 +302,10 @@ export class AssetRepository implements IAssetRepository {
|
||||
switch (property) {
|
||||
case WithoutProperty.THUMBNAIL: {
|
||||
where = [
|
||||
{ previewPath: IsNull(), isVisible: true },
|
||||
{ previewPath: '', isVisible: true },
|
||||
{ thumbnailPath: IsNull(), isVisible: true },
|
||||
{ thumbnailPath: '', isVisible: true },
|
||||
{ resizePath: IsNull(), isVisible: true },
|
||||
{ resizePath: '', isVisible: true },
|
||||
{ webpPath: IsNull(), isVisible: true },
|
||||
{ webpPath: '', isVisible: true },
|
||||
{ thumbhash: IsNull(), isVisible: true },
|
||||
];
|
||||
break;
|
||||
@@ -339,7 +339,7 @@ export class AssetRepository implements IAssetRepository {
|
||||
};
|
||||
where = {
|
||||
isVisible: true,
|
||||
previewPath: Not(IsNull()),
|
||||
resizePath: Not(IsNull()),
|
||||
smartSearch: {
|
||||
embedding: IsNull(),
|
||||
},
|
||||
@@ -352,7 +352,7 @@ export class AssetRepository implements IAssetRepository {
|
||||
smartInfo: true,
|
||||
};
|
||||
where = {
|
||||
previewPath: Not(IsNull()),
|
||||
resizePath: Not(IsNull()),
|
||||
isVisible: true,
|
||||
smartInfo: {
|
||||
tags: IsNull(),
|
||||
@@ -367,7 +367,7 @@ export class AssetRepository implements IAssetRepository {
|
||||
jobStatus: true,
|
||||
};
|
||||
where = {
|
||||
previewPath: Not(IsNull()),
|
||||
resizePath: Not(IsNull()),
|
||||
isVisible: true,
|
||||
faces: {
|
||||
assetId: IsNull(),
|
||||
@@ -385,7 +385,7 @@ export class AssetRepository implements IAssetRepository {
|
||||
faces: true,
|
||||
};
|
||||
where = {
|
||||
previewPath: Not(IsNull()),
|
||||
resizePath: Not(IsNull()),
|
||||
isVisible: true,
|
||||
faces: {
|
||||
assetId: Not(IsNull()),
|
||||
|
||||
@@ -35,8 +35,8 @@ export const JOBS_TO_QUEUE: Record<JobName, QueueName> = {
|
||||
|
||||
// thumbnails
|
||||
[JobName.QUEUE_GENERATE_THUMBNAILS]: QueueName.THUMBNAIL_GENERATION,
|
||||
[JobName.GENERATE_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
|
||||
[JobName.GENERATE_PREVIEW]: QueueName.THUMBNAIL_GENERATION,
|
||||
[JobName.GENERATE_JPEG_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
|
||||
[JobName.GENERATE_WEBP_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
|
||||
[JobName.GENERATE_THUMBHASH_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
|
||||
[JobName.GENERATE_PERSON_THUMBNAIL]: QueueName.THUMBNAIL_GENERATION,
|
||||
|
||||
|
||||
@@ -44,13 +44,13 @@ const _getAsset_1 = () => {
|
||||
asset_1.deviceId = 'device_id_1';
|
||||
asset_1.type = AssetType.VIDEO;
|
||||
asset_1.originalPath = 'fake_path/asset_1.jpeg';
|
||||
asset_1.previewPath = '';
|
||||
asset_1.resizePath = '';
|
||||
asset_1.fileModifiedAt = new Date('2022-06-19T23:41:36.910Z');
|
||||
asset_1.fileCreatedAt = new Date('2022-06-19T23:41:36.910Z');
|
||||
asset_1.updatedAt = new Date('2022-06-19T23:41:36.910Z');
|
||||
asset_1.isFavorite = false;
|
||||
asset_1.isArchived = false;
|
||||
asset_1.thumbnailPath = '';
|
||||
asset_1.webpPath = '';
|
||||
asset_1.encodedVideoPath = '';
|
||||
asset_1.duration = '0:00:00.000000';
|
||||
asset_1.exifInfo = new ExifEntity();
|
||||
|
||||
@@ -22,12 +22,12 @@ import {
|
||||
CheckExistingAssetsDto,
|
||||
CreateAssetDto,
|
||||
GetAssetThumbnailDto,
|
||||
GetAssetThumbnailFormatEnum,
|
||||
ServeFileDto,
|
||||
} from 'src/dtos/asset-v1.dto';
|
||||
import { AuthDto } from 'src/dtos/auth.dto';
|
||||
import { ASSET_CHECKSUM_CONSTRAINT, AssetEntity, AssetType } from 'src/entities/asset.entity';
|
||||
import { LibraryType } from 'src/entities/library.entity';
|
||||
import { ImageFormat } from 'src/entities/system-config.entity';
|
||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||
import { IAssetRepositoryV1 } from 'src/interfaces/asset-v1.interface';
|
||||
import { IAssetRepository } from 'src/interfaces/asset.interface';
|
||||
@@ -244,19 +244,19 @@ export class AssetServiceV1 {
|
||||
};
|
||||
}
|
||||
|
||||
private getThumbnailPath(asset: AssetEntity, format: ImageFormat) {
|
||||
private getThumbnailPath(asset: AssetEntity, format: GetAssetThumbnailFormatEnum) {
|
||||
switch (format) {
|
||||
case ImageFormat.WEBP: {
|
||||
if (asset.thumbnailPath) {
|
||||
return asset.thumbnailPath;
|
||||
case GetAssetThumbnailFormatEnum.WEBP: {
|
||||
if (asset.webpPath) {
|
||||
return asset.webpPath;
|
||||
}
|
||||
this.logger.warn(`WebP thumbnail requested but not found for asset ${asset.id}, falling back to JPEG`);
|
||||
}
|
||||
case ImageFormat.JPEG: {
|
||||
if (!asset.previewPath) {
|
||||
case GetAssetThumbnailFormatEnum.JPEG: {
|
||||
if (!asset.resizePath) {
|
||||
throw new NotFoundException(`No thumbnail found for asset ${asset.id}`);
|
||||
}
|
||||
return asset.previewPath;
|
||||
return asset.resizePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -268,12 +268,12 @@ export class AssetServiceV1 {
|
||||
* Serve file viewer on the web
|
||||
*/
|
||||
if (dto.isWeb && mimeType != 'image/gif') {
|
||||
if (!asset.previewPath) {
|
||||
if (!asset.resizePath) {
|
||||
this.logger.error('Error serving IMAGE asset for web');
|
||||
throw new InternalServerErrorException(`Failed to serve image asset for web`, 'ServeFile');
|
||||
}
|
||||
|
||||
return asset.previewPath;
|
||||
return asset.resizePath;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -283,15 +283,15 @@ export class AssetServiceV1 {
|
||||
return asset.originalPath;
|
||||
}
|
||||
|
||||
if (asset.thumbnailPath && asset.thumbnailPath.length > 0) {
|
||||
return asset.thumbnailPath;
|
||||
if (asset.webpPath && asset.webpPath.length > 0) {
|
||||
return asset.webpPath;
|
||||
}
|
||||
|
||||
if (!asset.previewPath) {
|
||||
throw new Error('previewPath not set');
|
||||
if (!asset.resizePath) {
|
||||
throw new Error('resizePath not set');
|
||||
}
|
||||
|
||||
return asset.previewPath;
|
||||
return asset.resizePath;
|
||||
}
|
||||
|
||||
private async getLibraryId(auth: AuthDto, libraryId?: string) {
|
||||
|
||||
@@ -777,8 +777,8 @@ describe(AssetService.name, () => {
|
||||
name: JobName.DELETE_FILES,
|
||||
data: {
|
||||
files: [
|
||||
assetWithFace.thumbnailPath,
|
||||
assetWithFace.previewPath,
|
||||
assetWithFace.webpPath,
|
||||
assetWithFace.resizePath,
|
||||
assetWithFace.encodedVideoPath,
|
||||
assetWithFace.sidecarPath,
|
||||
assetWithFace.originalPath,
|
||||
@@ -861,8 +861,8 @@ describe(AssetService.name, () => {
|
||||
name: JobName.DELETE_FILES,
|
||||
data: {
|
||||
files: [
|
||||
assetStub.external.thumbnailPath,
|
||||
assetStub.external.previewPath,
|
||||
assetStub.external.webpPath,
|
||||
assetStub.external.resizePath,
|
||||
assetStub.external.encodedVideoPath,
|
||||
assetStub.external.sidecarPath,
|
||||
],
|
||||
@@ -944,7 +944,9 @@ describe(AssetService.name, () => {
|
||||
it('should run the refresh thumbnails job', async () => {
|
||||
accessMock.asset.checkOwnerAccess.mockResolvedValue(new Set(['asset-1']));
|
||||
await sut.run(authStub.admin, { assetIds: ['asset-1'], name: AssetJobName.REGENERATE_THUMBNAIL }),
|
||||
expect(jobMock.queueAll).toHaveBeenCalledWith([{ name: JobName.GENERATE_THUMBNAIL, data: { id: 'asset-1' } }]);
|
||||
expect(jobMock.queueAll).toHaveBeenCalledWith([
|
||||
{ name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-1' } },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should run the transcode video', async () => {
|
||||
|
||||
@@ -461,7 +461,7 @@ export class AssetService {
|
||||
await this.jobRepository.queue({ name: JobName.ASSET_DELETION, data: { id: asset.livePhotoVideoId } });
|
||||
}
|
||||
|
||||
const files = [asset.thumbnailPath, asset.previewPath, asset.encodedVideoPath, asset.sidecarPath];
|
||||
const files = [asset.webpPath, asset.resizePath, asset.encodedVideoPath, asset.sidecarPath];
|
||||
if (!fromExternal) {
|
||||
files.push(asset.originalPath);
|
||||
}
|
||||
@@ -534,7 +534,7 @@ export class AssetService {
|
||||
}
|
||||
|
||||
case AssetJobName.REGENERATE_THUMBNAIL: {
|
||||
jobs.push({ name: JobName.GENERATE_THUMBNAIL, data: { id } });
|
||||
jobs.push({ name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id } });
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -95,13 +95,13 @@ export class AuditService {
|
||||
break;
|
||||
}
|
||||
|
||||
case AssetPathType.PREVIEW: {
|
||||
await this.assetRepository.update({ id, previewPath: pathValue });
|
||||
case AssetPathType.JPEG_THUMBNAIL: {
|
||||
await this.assetRepository.update({ id, resizePath: pathValue });
|
||||
break;
|
||||
}
|
||||
|
||||
case AssetPathType.THUMBNAIL: {
|
||||
await this.assetRepository.update({ id, thumbnailPath: pathValue });
|
||||
case AssetPathType.WEBP_THUMBNAIL: {
|
||||
await this.assetRepository.update({ id, webpPath: pathValue });
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -174,8 +174,8 @@ export class AuditService {
|
||||
const orphans: FileReportItemDto[] = [];
|
||||
for await (const assets of pagination) {
|
||||
assetCount += assets.length;
|
||||
for (const { id, originalPath, previewPath, encodedVideoPath, thumbnailPath, isExternal, checksum } of assets) {
|
||||
for (const file of [originalPath, previewPath, encodedVideoPath, thumbnailPath]) {
|
||||
for (const { id, originalPath, resizePath, encodedVideoPath, webpPath, isExternal, checksum } of assets) {
|
||||
for (const file of [originalPath, resizePath, encodedVideoPath, webpPath]) {
|
||||
track(file);
|
||||
}
|
||||
|
||||
@@ -191,14 +191,14 @@ export class AuditService {
|
||||
) {
|
||||
orphans.push({ ...entity, pathType: AssetPathType.ORIGINAL, pathValue: originalPath });
|
||||
}
|
||||
if (previewPath && !hasFile(thumbFiles, previewPath)) {
|
||||
orphans.push({ ...entity, pathType: AssetPathType.PREVIEW, pathValue: previewPath });
|
||||
if (resizePath && !hasFile(thumbFiles, resizePath)) {
|
||||
orphans.push({ ...entity, pathType: AssetPathType.JPEG_THUMBNAIL, pathValue: resizePath });
|
||||
}
|
||||
if (thumbnailPath && !hasFile(thumbFiles, thumbnailPath)) {
|
||||
orphans.push({ ...entity, pathType: AssetPathType.THUMBNAIL, pathValue: thumbnailPath });
|
||||
if (webpPath && !hasFile(thumbFiles, webpPath)) {
|
||||
orphans.push({ ...entity, pathType: AssetPathType.WEBP_THUMBNAIL, pathValue: webpPath });
|
||||
}
|
||||
if (encodedVideoPath && !hasFile(videoFiles, encodedVideoPath)) {
|
||||
orphans.push({ ...entity, pathType: AssetPathType.THUMBNAIL, pathValue: encodedVideoPath });
|
||||
orphans.push({ ...entity, pathType: AssetPathType.WEBP_THUMBNAIL, pathValue: encodedVideoPath });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,7 +275,7 @@ describe(JobService.name, () => {
|
||||
},
|
||||
{
|
||||
item: { name: JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE, data: { id: 'asset-1', source: 'upload' } },
|
||||
jobs: [JobName.GENERATE_THUMBNAIL],
|
||||
jobs: [JobName.GENERATE_JPEG_THUMBNAIL],
|
||||
},
|
||||
{
|
||||
item: { name: JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE, data: { id: 'asset-1' } },
|
||||
@@ -286,13 +286,13 @@ describe(JobService.name, () => {
|
||||
jobs: [],
|
||||
},
|
||||
{
|
||||
item: { name: JobName.GENERATE_THUMBNAIL, data: { id: 'asset-1' } },
|
||||
jobs: [JobName.GENERATE_PREVIEW, JobName.GENERATE_THUMBHASH_THUMBNAIL],
|
||||
item: { name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-1' } },
|
||||
jobs: [JobName.GENERATE_WEBP_THUMBNAIL, JobName.GENERATE_THUMBHASH_THUMBNAIL],
|
||||
},
|
||||
{
|
||||
item: { name: JobName.GENERATE_THUMBNAIL, data: { id: 'asset-1', source: 'upload' } },
|
||||
item: { name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-1', source: 'upload' } },
|
||||
jobs: [
|
||||
JobName.GENERATE_PREVIEW,
|
||||
JobName.GENERATE_WEBP_THUMBNAIL,
|
||||
JobName.GENERATE_THUMBHASH_THUMBNAIL,
|
||||
JobName.SMART_SEARCH,
|
||||
JobName.FACE_DETECTION,
|
||||
@@ -300,9 +300,9 @@ describe(JobService.name, () => {
|
||||
],
|
||||
},
|
||||
{
|
||||
item: { name: JobName.GENERATE_THUMBNAIL, data: { id: 'asset-live-image', source: 'upload' } },
|
||||
item: { name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: 'asset-live-image', source: 'upload' } },
|
||||
jobs: [
|
||||
JobName.GENERATE_PREVIEW,
|
||||
JobName.GENERATE_WEBP_THUMBNAIL,
|
||||
JobName.GENERATE_THUMBHASH_THUMBNAIL,
|
||||
JobName.SMART_SEARCH,
|
||||
JobName.FACE_DETECTION,
|
||||
@@ -325,7 +325,7 @@ describe(JobService.name, () => {
|
||||
|
||||
for (const { item, jobs } of tests) {
|
||||
it(`should queue ${jobs.length} jobs when a ${item.name} job finishes successfully`, async () => {
|
||||
if (item.name === JobName.GENERATE_THUMBNAIL && item.data.source === 'upload') {
|
||||
if (item.name === JobName.GENERATE_JPEG_THUMBNAIL && item.data.source === 'upload') {
|
||||
if (item.data.id === 'asset-live-image') {
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.livePhotoStillAsset]);
|
||||
} else {
|
||||
|
||||
@@ -233,7 +233,7 @@ export class JobService {
|
||||
|
||||
case JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE: {
|
||||
if (item.data.source === 'upload') {
|
||||
await this.jobRepository.queue({ name: JobName.GENERATE_THUMBNAIL, data: item.data });
|
||||
await this.jobRepository.queue({ name: JobName.GENERATE_JPEG_THUMBNAIL, data: item.data });
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -247,9 +247,9 @@ export class JobService {
|
||||
break;
|
||||
}
|
||||
|
||||
case JobName.GENERATE_THUMBNAIL: {
|
||||
case JobName.GENERATE_JPEG_THUMBNAIL: {
|
||||
const jobs: JobItem[] = [
|
||||
{ name: JobName.GENERATE_PREVIEW, data: item.data },
|
||||
{ name: JobName.GENERATE_WEBP_THUMBNAIL, data: item.data },
|
||||
{ name: JobName.GENERATE_THUMBHASH_THUMBNAIL, data: item.data },
|
||||
];
|
||||
|
||||
@@ -270,7 +270,7 @@ export class JobService {
|
||||
break;
|
||||
}
|
||||
|
||||
case JobName.GENERATE_PREVIEW: {
|
||||
case JobName.GENERATE_WEBP_THUMBNAIL: {
|
||||
if (item.data.source !== 'upload') {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import { ExifEntity } from 'src/entities/exif.entity';
|
||||
import {
|
||||
AudioCodec,
|
||||
Colorspace,
|
||||
ImageFormat,
|
||||
SystemConfigKey,
|
||||
ToneMapping,
|
||||
TranscodeHWAccel,
|
||||
@@ -79,7 +78,7 @@ describe(MediaService.name, () => {
|
||||
expect(assetMock.getWithout).not.toHaveBeenCalled();
|
||||
expect(jobMock.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.GENERATE_THUMBNAIL,
|
||||
name: JobName.GENERATE_JPEG_THUMBNAIL,
|
||||
data: { id: assetStub.image.id },
|
||||
},
|
||||
]);
|
||||
@@ -137,7 +136,7 @@ describe(MediaService.name, () => {
|
||||
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL);
|
||||
expect(jobMock.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.GENERATE_THUMBNAIL,
|
||||
name: JobName.GENERATE_JPEG_THUMBNAIL,
|
||||
data: { id: assetStub.image.id },
|
||||
},
|
||||
]);
|
||||
@@ -161,7 +160,7 @@ describe(MediaService.name, () => {
|
||||
expect(assetMock.getWithout).toHaveBeenCalledWith({ skip: 0, take: 1000 }, WithoutProperty.THUMBNAIL);
|
||||
expect(jobMock.queueAll).toHaveBeenCalledWith([
|
||||
{
|
||||
name: JobName.GENERATE_PREVIEW,
|
||||
name: JobName.GENERATE_WEBP_THUMBNAIL,
|
||||
data: { id: assetStub.image.id },
|
||||
},
|
||||
]);
|
||||
@@ -194,10 +193,10 @@ describe(MediaService.name, () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleGeneratePreview', () => {
|
||||
describe('handleGenerateJpegThumbnail', () => {
|
||||
it('should skip thumbnail generation if asset not found', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([]);
|
||||
await sut.handleGeneratePreview({ id: assetStub.image.id });
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
|
||||
expect(mediaMock.resize).not.toHaveBeenCalled();
|
||||
expect(assetMock.update).not.toHaveBeenCalledWith();
|
||||
});
|
||||
@@ -205,29 +204,25 @@ describe(MediaService.name, () => {
|
||||
it('should skip video thumbnail generation if no video stream', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.noVideoStreams);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleGeneratePreview({ id: assetStub.image.id });
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
|
||||
expect(mediaMock.resize).not.toHaveBeenCalled();
|
||||
expect(assetMock.update).not.toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('should generate a thumbnail for an image', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
await sut.handleGeneratePreview({ id: assetStub.image.id });
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
|
||||
|
||||
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se');
|
||||
expect(mediaMock.resize).toHaveBeenCalledWith(
|
||||
'/original/path.jpg',
|
||||
'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
|
||||
{
|
||||
size: 1440,
|
||||
format: ImageFormat.JPEG,
|
||||
quality: 80,
|
||||
colorspace: Colorspace.SRGB,
|
||||
},
|
||||
);
|
||||
expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', 'upload/thumbs/user-id/as/se/asset-id.jpeg', {
|
||||
size: 1440,
|
||||
format: 'jpeg',
|
||||
quality: 80,
|
||||
colorspace: Colorspace.SRGB,
|
||||
});
|
||||
expect(assetMock.update).toHaveBeenCalledWith({
|
||||
id: 'asset-id',
|
||||
previewPath: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
|
||||
resizePath: 'upload/thumbs/user-id/as/se/asset-id.jpeg',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -235,34 +230,30 @@ describe(MediaService.name, () => {
|
||||
assetMock.getByIds.mockResolvedValue([
|
||||
{ ...assetStub.image, exifInfo: { profileDescription: 'Adobe RGB', bitsPerSample: 14 } as ExifEntity },
|
||||
]);
|
||||
await sut.handleGeneratePreview({ id: assetStub.image.id });
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
|
||||
|
||||
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se');
|
||||
expect(mediaMock.resize).toHaveBeenCalledWith(
|
||||
'/original/path.jpg',
|
||||
'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
|
||||
{
|
||||
size: 1440,
|
||||
format: ImageFormat.JPEG,
|
||||
quality: 80,
|
||||
colorspace: Colorspace.P3,
|
||||
},
|
||||
);
|
||||
expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', 'upload/thumbs/user-id/as/se/asset-id.jpeg', {
|
||||
size: 1440,
|
||||
format: 'jpeg',
|
||||
quality: 80,
|
||||
colorspace: Colorspace.P3,
|
||||
});
|
||||
expect(assetMock.update).toHaveBeenCalledWith({
|
||||
id: 'asset-id',
|
||||
previewPath: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
|
||||
resizePath: 'upload/thumbs/user-id/as/se/asset-id.jpeg',
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate a thumbnail for a video', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleGeneratePreview({ id: assetStub.video.id });
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetStub.video.id });
|
||||
|
||||
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se');
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
|
||||
'upload/thumbs/user-id/as/se/asset-id.jpeg',
|
||||
{
|
||||
inputOptions: ['-ss 00:00:00', '-sws_flags accurate_rnd+bitexact+full_chroma_int'],
|
||||
outputOptions: [
|
||||
@@ -275,19 +266,19 @@ describe(MediaService.name, () => {
|
||||
);
|
||||
expect(assetMock.update).toHaveBeenCalledWith({
|
||||
id: 'asset-id',
|
||||
previewPath: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
|
||||
resizePath: 'upload/thumbs/user-id/as/se/asset-id.jpeg',
|
||||
});
|
||||
});
|
||||
|
||||
it('should tonemap thumbnail for hdr video', async () => {
|
||||
mediaMock.probe.mockResolvedValue(probeStub.videoStreamHDR);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleGeneratePreview({ id: assetStub.video.id });
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetStub.video.id });
|
||||
|
||||
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se');
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
|
||||
'upload/thumbs/user-id/as/se/asset-id.jpeg',
|
||||
{
|
||||
inputOptions: ['-ss 00:00:00', '-sws_flags accurate_rnd+bitexact+full_chroma_int'],
|
||||
outputOptions: [
|
||||
@@ -300,7 +291,7 @@ describe(MediaService.name, () => {
|
||||
);
|
||||
expect(assetMock.update).toHaveBeenCalledWith({
|
||||
id: 'asset-id',
|
||||
previewPath: 'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
|
||||
resizePath: 'upload/thumbs/user-id/as/se/asset-id.jpeg',
|
||||
});
|
||||
});
|
||||
|
||||
@@ -311,11 +302,11 @@ describe(MediaService.name, () => {
|
||||
{ key: SystemConfigKey.FFMPEG_MAX_BITRATE, value: '5000k' },
|
||||
]);
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.video]);
|
||||
await sut.handleGeneratePreview({ id: assetStub.video.id });
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetStub.video.id });
|
||||
|
||||
expect(mediaMock.transcode).toHaveBeenCalledWith(
|
||||
'/original/path.ext',
|
||||
'upload/thumbs/user-id/as/se/asset-id-preview.jpeg',
|
||||
'upload/thumbs/user-id/as/se/asset-id.jpeg',
|
||||
{
|
||||
inputOptions: ['-ss 00:00:00', '-sws_flags accurate_rnd+bitexact+full_chroma_int'],
|
||||
outputOptions: [
|
||||
@@ -330,35 +321,31 @@ describe(MediaService.name, () => {
|
||||
|
||||
it('should run successfully', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
await sut.handleGeneratePreview({ id: assetStub.image.id });
|
||||
await sut.handleGenerateJpegThumbnail({ id: assetStub.image.id });
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleGenerateThumbnail', () => {
|
||||
describe('handleGenerateWebpThumbnail', () => {
|
||||
it('should skip thumbnail generation if asset not found', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([]);
|
||||
await sut.handleGenerateThumbnail({ id: assetStub.image.id });
|
||||
await sut.handleGenerateWebpThumbnail({ id: assetStub.image.id });
|
||||
expect(mediaMock.resize).not.toHaveBeenCalled();
|
||||
expect(assetMock.update).not.toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('should generate a thumbnail', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
await sut.handleGenerateThumbnail({ id: assetStub.image.id });
|
||||
await sut.handleGenerateWebpThumbnail({ id: assetStub.image.id });
|
||||
|
||||
expect(mediaMock.resize).toHaveBeenCalledWith(
|
||||
'/original/path.jpg',
|
||||
'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp',
|
||||
{
|
||||
format: ImageFormat.WEBP,
|
||||
size: 250,
|
||||
quality: 80,
|
||||
colorspace: Colorspace.SRGB,
|
||||
},
|
||||
);
|
||||
expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', 'upload/thumbs/user-id/as/se/asset-id.webp', {
|
||||
format: 'webp',
|
||||
size: 250,
|
||||
quality: 80,
|
||||
colorspace: Colorspace.SRGB,
|
||||
});
|
||||
expect(assetMock.update).toHaveBeenCalledWith({
|
||||
id: 'asset-id',
|
||||
thumbnailPath: 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp',
|
||||
webpPath: 'upload/thumbs/user-id/as/se/asset-id.webp',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -367,35 +354,31 @@ describe(MediaService.name, () => {
|
||||
assetMock.getByIds.mockResolvedValue([
|
||||
{ ...assetStub.image, exifInfo: { profileDescription: 'Adobe RGB', bitsPerSample: 14 } as ExifEntity },
|
||||
]);
|
||||
await sut.handleGenerateThumbnail({ id: assetStub.image.id });
|
||||
await sut.handleGenerateWebpThumbnail({ id: assetStub.image.id });
|
||||
|
||||
expect(storageMock.mkdirSync).toHaveBeenCalledWith('upload/thumbs/user-id/as/se');
|
||||
expect(mediaMock.resize).toHaveBeenCalledWith(
|
||||
'/original/path.jpg',
|
||||
'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp',
|
||||
{
|
||||
format: ImageFormat.WEBP,
|
||||
size: 250,
|
||||
quality: 80,
|
||||
colorspace: Colorspace.P3,
|
||||
},
|
||||
);
|
||||
expect(mediaMock.resize).toHaveBeenCalledWith('/original/path.jpg', 'upload/thumbs/user-id/as/se/asset-id.webp', {
|
||||
format: 'webp',
|
||||
size: 250,
|
||||
quality: 80,
|
||||
colorspace: Colorspace.P3,
|
||||
});
|
||||
expect(assetMock.update).toHaveBeenCalledWith({
|
||||
id: 'asset-id',
|
||||
thumbnailPath: 'upload/thumbs/user-id/as/se/asset-id-thumbnail.webp',
|
||||
webpPath: 'upload/thumbs/user-id/as/se/asset-id.webp',
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleGenerateThumbhashThumbnail', () => {
|
||||
it('should skip thumbhash generation if asset not found', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([]);
|
||||
await sut.handleGenerateThumbhash({ id: assetStub.image.id });
|
||||
await sut.handleGenerateThumbhashThumbnail({ id: assetStub.image.id });
|
||||
expect(mediaMock.generateThumbhash).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should skip thumbhash generation if resize path is missing', async () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.noResizePath]);
|
||||
await sut.handleGenerateThumbhash({ id: assetStub.noResizePath.id });
|
||||
await sut.handleGenerateThumbhashThumbnail({ id: assetStub.noResizePath.id });
|
||||
expect(mediaMock.generateThumbhash).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -404,7 +387,7 @@ describe(MediaService.name, () => {
|
||||
assetMock.getByIds.mockResolvedValue([assetStub.image]);
|
||||
mediaMock.generateThumbhash.mockResolvedValue(thumbhashBuffer);
|
||||
|
||||
await sut.handleGenerateThumbhash({ id: assetStub.image.id });
|
||||
await sut.handleGenerateThumbhashThumbnail({ id: assetStub.image.id });
|
||||
|
||||
expect(mediaMock.generateThumbhash).toHaveBeenCalledWith('/uploads/user-id/thumbs/path.jpg');
|
||||
expect(assetMock.update).toHaveBeenCalledWith({ id: 'asset-id', thumbhash: thumbhashBuffer });
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Inject, Injectable, UnsupportedMediaTypeException } from '@nestjs/common';
|
||||
import { GeneratedImageType, StorageCore, StorageFolder } from 'src/cores/storage.core';
|
||||
import { StorageCore, StorageFolder } from 'src/cores/storage.core';
|
||||
import { SystemConfigCore } from 'src/cores/system-config.core';
|
||||
import { SystemConfigFFmpegDto } from 'src/dtos/system-config.dto';
|
||||
import { AssetEntity, AssetType } from 'src/entities/asset.entity';
|
||||
@@ -7,7 +7,6 @@ import { AssetPathType } from 'src/entities/move.entity';
|
||||
import {
|
||||
AudioCodec,
|
||||
Colorspace,
|
||||
ImageFormat,
|
||||
TranscodeHWAccel,
|
||||
TranscodePolicy,
|
||||
TranscodeTarget,
|
||||
@@ -82,12 +81,12 @@ export class MediaService {
|
||||
const jobs: JobItem[] = [];
|
||||
|
||||
for (const asset of assets) {
|
||||
if (!asset.previewPath || force) {
|
||||
jobs.push({ name: JobName.GENERATE_THUMBNAIL, data: { id: asset.id } });
|
||||
if (!asset.resizePath || force) {
|
||||
jobs.push({ name: JobName.GENERATE_JPEG_THUMBNAIL, data: { id: asset.id } });
|
||||
continue;
|
||||
}
|
||||
if (!asset.thumbnailPath) {
|
||||
jobs.push({ name: JobName.GENERATE_PREVIEW, data: { id: asset.id } });
|
||||
if (!asset.webpPath) {
|
||||
jobs.push({ name: JobName.GENERATE_WEBP_THUMBNAIL, data: { id: asset.id } });
|
||||
}
|
||||
if (!asset.thumbhash) {
|
||||
jobs.push({ name: JobName.GENERATE_THUMBHASH_THUMBNAIL, data: { id: asset.id } });
|
||||
@@ -153,41 +152,41 @@ export class MediaService {
|
||||
}
|
||||
|
||||
async handleAssetMigration({ id }: IEntityJob): Promise<JobStatus> {
|
||||
const { image } = await this.configCore.getConfig();
|
||||
const [asset] = await this.assetRepository.getByIds([id]);
|
||||
if (!asset) {
|
||||
return JobStatus.FAILED;
|
||||
}
|
||||
|
||||
await this.storageCore.moveAssetImage(asset, AssetPathType.PREVIEW, image.previewFormat);
|
||||
await this.storageCore.moveAssetImage(asset, AssetPathType.THUMBNAIL, image.thumbnailFormat);
|
||||
await this.storageCore.moveAssetVideo(asset);
|
||||
await this.storageCore.moveAssetFile(asset, AssetPathType.JPEG_THUMBNAIL);
|
||||
await this.storageCore.moveAssetFile(asset, AssetPathType.WEBP_THUMBNAIL);
|
||||
await this.storageCore.moveAssetFile(asset, AssetPathType.ENCODED_VIDEO);
|
||||
|
||||
return JobStatus.SUCCESS;
|
||||
}
|
||||
|
||||
async handleGeneratePreview({ id }: IEntityJob): Promise<JobStatus> {
|
||||
async handleGenerateJpegThumbnail({ id }: IEntityJob): Promise<JobStatus> {
|
||||
const [asset] = await this.assetRepository.getByIds([id], { exifInfo: true });
|
||||
if (!asset) {
|
||||
return JobStatus.FAILED;
|
||||
}
|
||||
|
||||
const previewPath = await this.generateThumbnail(asset, AssetPathType.PREVIEW, ImageFormat.JPEG);
|
||||
await this.assetRepository.update({ id: asset.id, previewPath });
|
||||
const resizePath = await this.generateThumbnail(asset, 'jpeg');
|
||||
await this.assetRepository.update({ id: asset.id, resizePath });
|
||||
return JobStatus.SUCCESS;
|
||||
}
|
||||
|
||||
private async generateThumbnail(asset: AssetEntity, type: GeneratedImageType, format: ImageFormat) {
|
||||
const { image, ffmpeg } = await this.configCore.getConfig();
|
||||
const size = type === AssetPathType.PREVIEW ? image.previewSize : image.thumbnailSize;
|
||||
const path = StorageCore.getImagePath(asset, type, format);
|
||||
private async generateThumbnail(asset: AssetEntity, format: 'jpeg' | 'webp') {
|
||||
const { thumbnail, ffmpeg } = await this.configCore.getConfig();
|
||||
const size = format === 'jpeg' ? thumbnail.jpegSize : thumbnail.webpSize;
|
||||
const path =
|
||||
format === 'jpeg' ? StorageCore.getLargeThumbnailPath(asset) : StorageCore.getSmallThumbnailPath(asset);
|
||||
this.storageCore.ensureFolders(path);
|
||||
|
||||
switch (asset.type) {
|
||||
case AssetType.IMAGE: {
|
||||
const colorspace = this.isSRGB(asset) ? Colorspace.SRGB : image.colorspace;
|
||||
const imageOptions = { format, size, colorspace, quality: image.quality };
|
||||
await this.mediaRepository.resize(asset.originalPath, path, imageOptions);
|
||||
const colorspace = this.isSRGB(asset) ? Colorspace.SRGB : thumbnail.colorspace;
|
||||
const thumbnailOptions = { format, size, colorspace, quality: thumbnail.quality };
|
||||
await this.mediaRepository.resize(asset.originalPath, path, thumbnailOptions);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -215,24 +214,24 @@ export class MediaService {
|
||||
return path;
|
||||
}
|
||||
|
||||
async handleGenerateThumbnail({ id }: IEntityJob): Promise<JobStatus> {
|
||||
async handleGenerateWebpThumbnail({ id }: IEntityJob): Promise<JobStatus> {
|
||||
const [asset] = await this.assetRepository.getByIds([id], { exifInfo: true });
|
||||
if (!asset) {
|
||||
return JobStatus.FAILED;
|
||||
}
|
||||
|
||||
const thumbnailPath = await this.generateThumbnail(asset, AssetPathType.THUMBNAIL, ImageFormat.WEBP);
|
||||
await this.assetRepository.update({ id: asset.id, thumbnailPath });
|
||||
const webpPath = await this.generateThumbnail(asset, 'webp');
|
||||
await this.assetRepository.update({ id: asset.id, webpPath });
|
||||
return JobStatus.SUCCESS;
|
||||
}
|
||||
|
||||
async handleGenerateThumbhash({ id }: IEntityJob): Promise<JobStatus> {
|
||||
async handleGenerateThumbhashThumbnail({ id }: IEntityJob): Promise<JobStatus> {
|
||||
const [asset] = await this.assetRepository.getByIds([id]);
|
||||
if (!asset?.previewPath) {
|
||||
if (!asset?.resizePath) {
|
||||
return JobStatus.FAILED;
|
||||
}
|
||||
|
||||
const thumbhash = await this.mediaRepository.generateThumbhash(asset.previewPath);
|
||||
const thumbhash = await this.mediaRepository.generateThumbhash(asset.resizePath);
|
||||
await this.assetRepository.update({ id: asset.id, thumbhash });
|
||||
|
||||
return JobStatus.SUCCESS;
|
||||
|
||||
@@ -53,9 +53,9 @@ export class MicroservicesService {
|
||||
[JobName.MIGRATE_ASSET]: (data) => this.mediaService.handleAssetMigration(data),
|
||||
[JobName.MIGRATE_PERSON]: (data) => this.personService.handlePersonMigration(data),
|
||||
[JobName.QUEUE_GENERATE_THUMBNAILS]: (data) => this.mediaService.handleQueueGenerateThumbnails(data),
|
||||
[JobName.GENERATE_THUMBNAIL]: (data) => this.mediaService.handleGeneratePreview(data),
|
||||
[JobName.GENERATE_PREVIEW]: (data) => this.mediaService.handleGenerateThumbnail(data),
|
||||
[JobName.GENERATE_THUMBHASH_THUMBNAIL]: (data) => this.mediaService.handleGenerateThumbhash(data),
|
||||
[JobName.GENERATE_JPEG_THUMBNAIL]: (data) => this.mediaService.handleGenerateJpegThumbnail(data),
|
||||
[JobName.GENERATE_WEBP_THUMBNAIL]: (data) => this.mediaService.handleGenerateWebpThumbnail(data),
|
||||
[JobName.GENERATE_THUMBHASH_THUMBNAIL]: (data) => this.mediaService.handleGenerateThumbhashThumbnail(data),
|
||||
[JobName.QUEUE_VIDEO_CONVERSION]: (data) => this.mediaService.handleQueueVideoConversion(data),
|
||||
[JobName.VIDEO_CONVERSION]: (data) => this.mediaService.handleVideoConversion(data),
|
||||
[JobName.QUEUE_METADATA_EXTRACTION]: (data) => this.metadataService.handleQueueMetadataExtraction(data),
|
||||
|
||||
@@ -645,7 +645,7 @@ describe(PersonService.name, () => {
|
||||
expect(machineLearningMock.detectFaces).toHaveBeenCalledWith(
|
||||
'http://immich-machine-learning:3003',
|
||||
{
|
||||
imagePath: assetStub.image.previewPath,
|
||||
imagePath: assetStub.image.resizePath,
|
||||
},
|
||||
{
|
||||
enabled: true,
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
} from 'src/dtos/person.dto';
|
||||
import { PersonPathType } from 'src/entities/move.entity';
|
||||
import { PersonEntity } from 'src/entities/person.entity';
|
||||
import { ImageFormat } from 'src/entities/system-config.entity';
|
||||
import { IAccessRepository } from 'src/interfaces/access.interface';
|
||||
import { IAssetRepository, WithoutProperty } from 'src/interfaces/asset.interface';
|
||||
import { ICryptoRepository } from 'src/interfaces/crypto.interface';
|
||||
@@ -316,17 +315,17 @@ export class PersonService {
|
||||
},
|
||||
};
|
||||
const [asset] = await this.assetRepository.getByIds([id], relations);
|
||||
if (!asset || !asset.previewPath || asset.faces?.length > 0) {
|
||||
if (!asset || !asset.resizePath || asset.faces?.length > 0) {
|
||||
return JobStatus.FAILED;
|
||||
}
|
||||
|
||||
const faces = await this.machineLearningRepository.detectFaces(
|
||||
machineLearning.url,
|
||||
{ imagePath: asset.previewPath },
|
||||
{ imagePath: asset.resizePath },
|
||||
machineLearning.facialRecognition,
|
||||
);
|
||||
|
||||
this.logger.debug(`${faces.length} faces detected in ${asset.previewPath}`);
|
||||
this.logger.debug(`${faces.length} faces detected in ${asset.resizePath}`);
|
||||
this.logger.verbose(faces.map((face) => ({ ...face, embedding: `vector(${face.embedding.length})` })));
|
||||
|
||||
if (faces.length > 0) {
|
||||
@@ -471,7 +470,7 @@ export class PersonService {
|
||||
}
|
||||
|
||||
async handleGeneratePersonThumbnail(data: IEntityJob): Promise<JobStatus> {
|
||||
const { machineLearning, image } = await this.configCore.getConfig();
|
||||
const { machineLearning, thumbnail } = await this.configCore.getConfig();
|
||||
if (!machineLearning.enabled || !machineLearning.facialRecognition.enabled) {
|
||||
return JobStatus.SKIPPED;
|
||||
}
|
||||
@@ -497,7 +496,7 @@ export class PersonService {
|
||||
} = face;
|
||||
|
||||
const [asset] = await this.assetRepository.getByIds([assetId]);
|
||||
if (!asset?.previewPath) {
|
||||
if (!asset?.resizePath) {
|
||||
return JobStatus.FAILED;
|
||||
}
|
||||
this.logger.verbose(`Cropping face for person: ${person.id}`);
|
||||
@@ -528,12 +527,12 @@ export class PersonService {
|
||||
height: newHalfSize * 2,
|
||||
};
|
||||
|
||||
const croppedOutput = await this.mediaRepository.crop(asset.previewPath, cropOptions);
|
||||
const croppedOutput = await this.mediaRepository.crop(asset.resizePath, cropOptions);
|
||||
const thumbnailOptions = {
|
||||
format: ImageFormat.JPEG,
|
||||
format: 'jpeg',
|
||||
size: FACE_THUMBNAIL_SIZE,
|
||||
colorspace: image.colorspace,
|
||||
quality: image.quality,
|
||||
colorspace: thumbnail.colorspace,
|
||||
quality: thumbnail.quality,
|
||||
} as const;
|
||||
|
||||
await this.mediaRepository.resize(croppedOutput, thumbnailPath, thumbnailOptions);
|
||||
|
||||
@@ -18,7 +18,7 @@ import { newSystemConfigRepositoryMock } from 'test/repositories/system-config.r
|
||||
|
||||
const asset = {
|
||||
id: 'asset-1',
|
||||
previewPath: 'path/to/resize.ext',
|
||||
resizePath: 'path/to/resize.ext',
|
||||
} as AssetEntity;
|
||||
|
||||
describe(SmartInfoService.name, () => {
|
||||
@@ -94,7 +94,7 @@ describe(SmartInfoService.name, () => {
|
||||
});
|
||||
|
||||
it('should skip assets without a resize path', async () => {
|
||||
const asset = { previewPath: '' } as AssetEntity;
|
||||
const asset = { resizePath: '' } as AssetEntity;
|
||||
assetMock.getByIds.mockResolvedValue([asset]);
|
||||
|
||||
await sut.handleEncodeClip({ id: asset.id });
|
||||
|
||||
@@ -83,13 +83,13 @@ export class SmartInfoService {
|
||||
return JobStatus.FAILED;
|
||||
}
|
||||
|
||||
if (!asset.previewPath) {
|
||||
if (!asset.resizePath) {
|
||||
return JobStatus.FAILED;
|
||||
}
|
||||
|
||||
const clipEmbedding = await this.machineLearning.encodeImage(
|
||||
machineLearning.url,
|
||||
{ imagePath: asset.previewPath },
|
||||
{ imagePath: asset.resizePath },
|
||||
machineLearning.clip,
|
||||
);
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
AudioCodec,
|
||||
CQMode,
|
||||
Colorspace,
|
||||
ImageFormat,
|
||||
LogLevel,
|
||||
SystemConfig,
|
||||
SystemConfigEntity,
|
||||
@@ -120,11 +119,9 @@ const updatedConfig = Object.freeze<SystemConfig>({
|
||||
hashVerificationEnabled: true,
|
||||
template: '{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}',
|
||||
},
|
||||
image: {
|
||||
thumbnailFormat: ImageFormat.WEBP,
|
||||
thumbnailSize: 250,
|
||||
previewFormat: ImageFormat.JPEG,
|
||||
previewSize: 1440,
|
||||
thumbnail: {
|
||||
webpSize: 250,
|
||||
jpegSize: 1440,
|
||||
quality: 80,
|
||||
colorspace: Colorspace.P3,
|
||||
},
|
||||
|
||||
@@ -58,7 +58,7 @@ export function searchAssetBuilder(
|
||||
builder.andWhere(`${builder.alias}.ownerId IN (:...userIds)`, { userIds: options.userIds });
|
||||
}
|
||||
|
||||
const path = _.pick(options, ['encodedVideoPath', 'originalPath', 'previewPath', 'thumbnailPath']);
|
||||
const path = _.pick(options, ['encodedVideoPath', 'originalPath', 'resizePath', 'webpPath']);
|
||||
builder.andWhere(_.omitBy(path, _.isUndefined));
|
||||
|
||||
if (options.originalFileName) {
|
||||
|
||||
+1
-1
@@ -17,4 +17,4 @@ read_file_and_export "DB_USERNAME_FILE" "DB_USERNAME"
|
||||
read_file_and_export "DB_PASSWORD_FILE" "DB_PASSWORD"
|
||||
read_file_and_export "REDIS_PASSWORD_FILE" "REDIS_PASSWORD"
|
||||
|
||||
exec node /usr/src/app/dist/main "$@"
|
||||
exec ~/.bun/bin/bun run /usr/src/app/dist/main "$@"
|
||||
|
||||
Vendored
+30
-30
@@ -26,10 +26,10 @@ export const assetStub = {
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: 'upload/library/IMG_123.jpg',
|
||||
previewPath: null,
|
||||
resizePath: null,
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
thumbnailPath: '/uploads/user-id/webp/path.ext',
|
||||
webpPath: '/uploads/user-id/webp/path.ext',
|
||||
thumbhash: Buffer.from('blablabla', 'base64'),
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
@@ -62,10 +62,10 @@ export const assetStub = {
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: 'upload/library/IMG_456.jpg',
|
||||
previewPath: '/uploads/user-id/thumbs/path.ext',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
thumbnailPath: null,
|
||||
webpPath: null,
|
||||
thumbhash: Buffer.from('blablabla', 'base64'),
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
@@ -102,10 +102,10 @@ export const assetStub = {
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.ext',
|
||||
previewPath: '/uploads/user-id/thumbs/path.ext',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
thumbnailPath: '/uploads/user-id/webp/path.ext',
|
||||
webpPath: '/uploads/user-id/webp/path.ext',
|
||||
thumbhash: null,
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
@@ -139,10 +139,10 @@ export const assetStub = {
|
||||
ownerId: 'admin-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.jpg',
|
||||
previewPath: '/uploads/admin-id/thumbs/path.jpg',
|
||||
resizePath: '/uploads/admin-id/thumbs/path.jpg',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
thumbnailPath: '/uploads/admin-id/webp/path.ext',
|
||||
webpPath: '/uploads/admin-id/webp/path.ext',
|
||||
thumbhash: Buffer.from('blablabla', 'base64'),
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
@@ -184,10 +184,10 @@ export const assetStub = {
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.jpg',
|
||||
previewPath: '/uploads/user-id/thumbs/path.jpg',
|
||||
resizePath: '/uploads/user-id/thumbs/path.jpg',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
thumbnailPath: '/uploads/user-id/webp/path.ext',
|
||||
webpPath: '/uploads/user-id/webp/path.ext',
|
||||
thumbhash: Buffer.from('blablabla', 'base64'),
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
@@ -224,10 +224,10 @@ export const assetStub = {
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/data/user1/photo.jpg',
|
||||
previewPath: '/uploads/user-id/thumbs/path.jpg',
|
||||
resizePath: '/uploads/user-id/thumbs/path.jpg',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
thumbnailPath: '/uploads/user-id/webp/path.ext',
|
||||
webpPath: '/uploads/user-id/webp/path.ext',
|
||||
thumbhash: Buffer.from('blablabla', 'base64'),
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
@@ -264,10 +264,10 @@ export const assetStub = {
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.jpg',
|
||||
previewPath: '/uploads/user-id/thumbs/path.jpg',
|
||||
resizePath: '/uploads/user-id/thumbs/path.jpg',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
thumbnailPath: '/uploads/user-id/webp/path.ext',
|
||||
webpPath: '/uploads/user-id/webp/path.ext',
|
||||
thumbhash: Buffer.from('blablabla', 'base64'),
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
@@ -304,10 +304,10 @@ export const assetStub = {
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.ext',
|
||||
previewPath: '/uploads/user-id/thumbs/path.ext',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
thumbnailPath: '/uploads/user-id/webp/path.ext',
|
||||
webpPath: '/uploads/user-id/webp/path.ext',
|
||||
thumbhash: Buffer.from('blablabla', 'base64'),
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
@@ -344,10 +344,10 @@ export const assetStub = {
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.ext',
|
||||
previewPath: '/uploads/user-id/thumbs/path.ext',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
thumbnailPath: '/uploads/user-id/webp/path.ext',
|
||||
webpPath: '/uploads/user-id/webp/path.ext',
|
||||
thumbhash: Buffer.from('blablabla', 'base64'),
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2015-02-23T05:06:29.716Z'),
|
||||
@@ -385,10 +385,10 @@ export const assetStub = {
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.ext',
|
||||
previewPath: '/uploads/user-id/thumbs/path.ext',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.VIDEO,
|
||||
thumbnailPath: null,
|
||||
webpPath: null,
|
||||
thumbhash: null,
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
@@ -456,10 +456,10 @@ export const assetStub = {
|
||||
deviceId: 'device-id',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
originalPath: '/original/path.ext',
|
||||
previewPath: '/uploads/user-id/thumbs/path.ext',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
sidecarPath: null,
|
||||
type: AssetType.IMAGE,
|
||||
thumbnailPath: null,
|
||||
webpPath: null,
|
||||
thumbhash: null,
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-22T05:06:29.716Z'),
|
||||
@@ -499,11 +499,11 @@ export const assetStub = {
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.ext',
|
||||
previewPath: '/uploads/user-id/thumbs/path.ext',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
thumbhash: null,
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
thumbnailPath: null,
|
||||
webpPath: null,
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
@@ -535,11 +535,11 @@ export const assetStub = {
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.ext',
|
||||
previewPath: '/uploads/user-id/thumbs/path.ext',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
thumbhash: null,
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
thumbnailPath: null,
|
||||
webpPath: null,
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
@@ -572,11 +572,11 @@ export const assetStub = {
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.ext',
|
||||
previewPath: '/uploads/user-id/thumbs/path.ext',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
thumbhash: null,
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.IMAGE,
|
||||
thumbnailPath: null,
|
||||
webpPath: null,
|
||||
encodedVideoPath: null,
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
updatedAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
@@ -610,10 +610,10 @@ export const assetStub = {
|
||||
ownerId: 'user-id',
|
||||
deviceId: 'device-id',
|
||||
originalPath: '/original/path.ext',
|
||||
previewPath: '/uploads/user-id/thumbs/path.ext',
|
||||
resizePath: '/uploads/user-id/thumbs/path.ext',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
type: AssetType.VIDEO,
|
||||
thumbnailPath: null,
|
||||
webpPath: null,
|
||||
thumbhash: null,
|
||||
encodedVideoPath: '/encoded/video/path.mp4',
|
||||
createdAt: new Date('2023-02-23T05:06:29.716Z'),
|
||||
|
||||
+2
-2
@@ -199,7 +199,7 @@ export const sharedLinkStub = {
|
||||
deviceId: 'device_id_1',
|
||||
type: AssetType.VIDEO,
|
||||
originalPath: 'fake_path/jpeg',
|
||||
previewPath: '',
|
||||
resizePath: '',
|
||||
checksum: Buffer.from('file hash', 'utf8'),
|
||||
fileModifiedAt: today,
|
||||
fileCreatedAt: today,
|
||||
@@ -219,7 +219,7 @@ export const sharedLinkStub = {
|
||||
objects: ['a', 'b', 'c'],
|
||||
asset: null as any,
|
||||
},
|
||||
thumbnailPath: '',
|
||||
webpPath: '',
|
||||
thumbhash: null,
|
||||
encodedVideoPath: '',
|
||||
duration: null,
|
||||
|
||||
+8
-36
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { Colorspace, ImageFormat, type SystemConfigDto } from '@immich/sdk';
|
||||
import { Colorspace, type SystemConfigDto } from '@immich/sdk';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
@@ -25,24 +25,10 @@
|
||||
<form autocomplete="off" on:submit|preventDefault>
|
||||
<div class="ml-4 mt-4 flex flex-col gap-4">
|
||||
<SettingSelect
|
||||
label="THUMBNAIL FORMAT"
|
||||
desc="Format used for generated thumbnail images. WebP produces smaller files at the same quality."
|
||||
number
|
||||
bind:value={config.thumbnail.thumbnailFormat}
|
||||
options={[
|
||||
{ value: ImageFormat.Jpeg, text: 'JPEG' },
|
||||
{ value: ImageFormat.Webp, text: 'WEBP' },
|
||||
]}
|
||||
name="resolution"
|
||||
isEdited={config.thumbnail.thumbnailFormat !== savedConfig.thumbnail.thumbnailFormat}
|
||||
{disabled}
|
||||
/>
|
||||
|
||||
<SettingSelect
|
||||
label="THUMBNAIL RESOLUTION"
|
||||
label="SMALL THUMBNAIL RESOLUTION"
|
||||
desc="Used when viewing groups of photos (main timeline, album view, etc.). Higher resolutions can preserve more detail but take longer to encode, have larger file sizes, and can reduce app responsiveness."
|
||||
number
|
||||
bind:value={config.thumbnail.thumbnailSize}
|
||||
bind:value={config.thumbnail.webpSize}
|
||||
options={[
|
||||
{ value: 1080, text: '1080p' },
|
||||
{ value: 720, text: '720p' },
|
||||
@@ -51,29 +37,15 @@
|
||||
{ value: 200, text: '200p' },
|
||||
]}
|
||||
name="resolution"
|
||||
isEdited={config.thumbnail.thumbnailSize !== savedConfig.thumbnail.thumbnailSize}
|
||||
isEdited={config.thumbnail.webpSize !== savedConfig.thumbnail.webpSize}
|
||||
{disabled}
|
||||
/>
|
||||
|
||||
<SettingSelect
|
||||
label="PREVIEW FORMAT"
|
||||
desc="Format used for generated preview images. WebP produces smaller files at the same quality."
|
||||
number
|
||||
bind:value={config.thumbnail.thumbnailFormat}
|
||||
options={[
|
||||
{ value: ImageFormat.Jpeg, text: 'JPEG' },
|
||||
{ value: ImageFormat.Webp, text: 'WEBP' },
|
||||
]}
|
||||
name="resolution"
|
||||
isEdited={config.thumbnail.thumbnailFormat !== savedConfig.thumbnail.thumbnailFormat}
|
||||
{disabled}
|
||||
/>
|
||||
|
||||
<SettingSelect
|
||||
label="PREVIEW RESOLUTION"
|
||||
label="LARGE THUMBNAIL RESOLUTION"
|
||||
desc="Used when viewing a single photo and for machine learning. Higher resolutions can preserve more detail but take longer to encode, have larger file sizes, and can reduce app responsiveness."
|
||||
number
|
||||
bind:value={config.thumbnail.previewSize}
|
||||
bind:value={config.thumbnail.jpegSize}
|
||||
options={[
|
||||
{ value: 2160, text: '4K' },
|
||||
{ value: 1440, text: '1440p' },
|
||||
@@ -81,14 +53,14 @@
|
||||
{ value: 720, text: '720p' },
|
||||
]}
|
||||
name="resolution"
|
||||
isEdited={config.thumbnail.previewSize !== savedConfig.thumbnail.previewSize}
|
||||
isEdited={config.thumbnail.jpegSize !== savedConfig.thumbnail.jpegSize}
|
||||
{disabled}
|
||||
/>
|
||||
|
||||
<SettingInputField
|
||||
inputType={SettingInputFieldType.NUMBER}
|
||||
label="QUALITY"
|
||||
desc="Image quality from 1-100. Higher is better for quality but produces larger files."
|
||||
desc="Thumbnail quality from 1-100. Higher is better for quality but produces larger files."
|
||||
bind:value={config.thumbnail.quality}
|
||||
isEdited={config.thumbnail.quality !== savedConfig.thumbnail.quality}
|
||||
/>
|
||||
@@ -13,7 +13,7 @@
|
||||
import SettingAccordion from '$lib/components/shared-components/settings/setting-accordion.svelte';
|
||||
import StorageTemplateSettings from '$lib/components/admin-page/settings/storage-template/storage-template-settings.svelte';
|
||||
import ThemeSettings from '$lib/components/admin-page/settings/theme/theme-settings.svelte';
|
||||
import ImageSettings from '$lib/components/admin-page/settings/image/image-settings.svelte';
|
||||
import ThumbnailSettings from '$lib/components/admin-page/settings/thumbnail/thumbnail-settings.svelte';
|
||||
import TrashSettings from '$lib/components/admin-page/settings/trash-settings/trash-settings.svelte';
|
||||
import UserSettings from '$lib/components/admin-page/settings/user-settings/user-settings.svelte';
|
||||
import LinkButton from '$lib/components/elements/buttons/link-button.svelte';
|
||||
@@ -43,7 +43,7 @@
|
||||
| typeof ServerSettings
|
||||
| typeof StorageTemplateSettings
|
||||
| typeof ThemeSettings
|
||||
| typeof ImageSettings
|
||||
| typeof ThumbnailSettings
|
||||
| typeof TrashSettings
|
||||
| typeof NewVersionCheckSettings
|
||||
| typeof FFmpegSettings
|
||||
@@ -64,12 +64,6 @@
|
||||
subtitle: string;
|
||||
key: string;
|
||||
}> = [
|
||||
{
|
||||
item: ImageSettings,
|
||||
title: 'Image Settings',
|
||||
subtitle: 'Manage the format, quality and resolution of generated images',
|
||||
key: 'image',
|
||||
},
|
||||
{
|
||||
item: JobSettings,
|
||||
title: 'Job Settings',
|
||||
@@ -130,6 +124,12 @@
|
||||
subtitle: 'Manage customization of the Immich web interface',
|
||||
key: 'theme',
|
||||
},
|
||||
{
|
||||
item: ThumbnailSettings,
|
||||
title: 'Thumbnail Settings',
|
||||
subtitle: 'Manage the resolution of thumbnail sizes',
|
||||
key: 'thumbnail',
|
||||
},
|
||||
{
|
||||
item: TrashSettings,
|
||||
title: 'Trash Settings',
|
||||
|
||||
Reference in New Issue
Block a user