Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f4993350a | ||
|
|
f9b1d1edaf | ||
|
|
cab5477656 | ||
|
|
b8de668f5f | ||
|
|
c5234731d6 | ||
|
|
ef86a77946 | ||
|
|
1b301984dd | ||
|
|
9807f76aff | ||
|
|
47673dd773 | ||
|
|
a9fb1d435a | ||
|
|
422ad20641 | ||
|
|
3ea2fe1c48 | ||
|
|
038e064e60 | ||
|
|
800f010383 | ||
|
|
4350f9363d | ||
|
|
76a1629e75 | ||
|
|
2493dfaba3 | ||
|
|
656dc08406 | ||
|
|
631f13cf2f | ||
|
|
9730bf0acc | ||
|
|
9f2b5ea86e | ||
|
|
5702442783 | ||
|
|
74c2f446e9 | ||
|
|
da1710bcd2 | ||
|
|
2dfd56b49b |
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
@@ -96,7 +96,11 @@ jobs:
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run svelte checks
|
||||
run: npm run check
|
||||
run: npm run check:svelte
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run tsc
|
||||
run: npm run check:typescript
|
||||
if: ${{ !cancelled() }}
|
||||
|
||||
- name: Run unit tests & coverage
|
||||
|
||||
@@ -3,7 +3,7 @@ version: "3.8"
|
||||
services:
|
||||
immich-server:
|
||||
container_name: immich_server
|
||||
image: ghcr.io/immich-app/immich-server:release
|
||||
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
|
||||
command: ["start-server.sh"]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
@@ -17,7 +17,7 @@ services:
|
||||
|
||||
immich-microservices:
|
||||
container_name: immich_microservices
|
||||
image: ghcr.io/immich-app/immich-server:release
|
||||
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
|
||||
command: ["start-microservices.sh"]
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
@@ -31,7 +31,7 @@ services:
|
||||
|
||||
immich-machine-learning:
|
||||
container_name: immich_machine_learning
|
||||
image: ghcr.io/immich-app/immich-machine-learning:release
|
||||
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
|
||||
volumes:
|
||||
- ${UPLOAD_LOCATION}:/usr/src/app/upload
|
||||
- model-cache:/cache
|
||||
@@ -41,7 +41,7 @@ services:
|
||||
|
||||
immich-web:
|
||||
container_name: immich_web
|
||||
image: ghcr.io/immich-app/immich-web:release
|
||||
image: ghcr.io/immich-app/immich-web:${IMMICH_VERSION:-release}
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
@@ -79,7 +79,7 @@ services:
|
||||
|
||||
immich-proxy:
|
||||
container_name: immich_proxy
|
||||
image: ghcr.io/immich-app/immich-proxy:release
|
||||
image: ghcr.io/immich-app/immich-proxy:${IMMICH_VERSION:-release}
|
||||
environment:
|
||||
# Make sure these values get passed through from the env file
|
||||
- IMMICH_SERVER_URL
|
||||
|
||||
@@ -105,3 +105,12 @@ IMMICH_MACHINE_LEARNING_URL=http://immich-machine-learning:3003
|
||||
####################################################################################
|
||||
|
||||
#IMMICH_API_URL_EXTERNAL=http://localhost:3001
|
||||
|
||||
###################################################################################
|
||||
# Immich Version - Optional
|
||||
#
|
||||
# This allows all immich docker images to be pinned to a specific version. By default,
|
||||
# the version is "release" but could be a specific version, like "v1.59.0".
|
||||
###################################################################################
|
||||
|
||||
#IMMICH_VERSION=
|
||||
|
||||
@@ -136,6 +136,15 @@ IMMICH_MACHINE_LEARNING_URL=http://immich-machine-learning:3003
|
||||
####################################################################################
|
||||
|
||||
#IMMICH_API_URL_EXTERNAL=http://localhost:3001
|
||||
|
||||
###################################################################################
|
||||
# Immich Version - Optional
|
||||
#
|
||||
# This allows all immich docker images to be pinned to a specific version. By default,
|
||||
# the version is "release" but could be a specific version, like "v1.59.0".
|
||||
###################################################################################
|
||||
|
||||
#IMMICH_VERSION=
|
||||
```
|
||||
|
||||
</details>
|
||||
@@ -159,6 +168,8 @@ For more information on how to use the application, please refer to the [Post In
|
||||
|
||||
### Step 4 - Upgrading
|
||||
|
||||
If `IMMICH_VERSION` is set, it will need to be updated to the latest or desired version.
|
||||
|
||||
When a new version of Immich is [released](https://github.com/immich-app/immich/releases), the application can be upgraded with the following commands, run in the directory with the `docker-compose.yml` file:
|
||||
|
||||
```bash title="Upgrade Immich"
|
||||
|
||||
@@ -22,13 +22,18 @@ class ClipRequestBody(BaseModel):
|
||||
classification_model = os.getenv(
|
||||
"MACHINE_LEARNING_CLASSIFICATION_MODEL", "microsoft/resnet-50"
|
||||
)
|
||||
object_model = os.getenv("MACHINE_LEARNING_OBJECT_MODEL", "hustvl/yolos-tiny")
|
||||
clip_image_model = os.getenv("MACHINE_LEARNING_CLIP_IMAGE_MODEL", "clip-ViT-B-32")
|
||||
clip_text_model = os.getenv("MACHINE_LEARNING_CLIP_TEXT_MODEL", "clip-ViT-B-32")
|
||||
facial_recognition_model = os.getenv(
|
||||
"MACHINE_LEARNING_FACIAL_RECOGNITION_MODEL", "buffalo_l"
|
||||
)
|
||||
|
||||
min_face_score = float(os.getenv("MACHINE_LEARNING_MIN_FACE_SCORE", 0.7))
|
||||
min_tag_score = float(os.getenv("MACHINE_LEARNING_MIN_TAG_SCORE", 0.9))
|
||||
eager_startup = (
|
||||
os.getenv("MACHINE_LEARNING_EAGER_STARTUP", "true") == "true"
|
||||
) # loads all models at startup
|
||||
|
||||
cache_folder = os.getenv("MACHINE_LEARNING_CACHE_FOLDER", "/cache")
|
||||
|
||||
_model_cache = {}
|
||||
@@ -38,12 +43,19 @@ app = FastAPI()
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
models = [
|
||||
(classification_model, "image-classification"),
|
||||
(clip_image_model, "clip"),
|
||||
(clip_text_model, "clip"),
|
||||
(facial_recognition_model, "facial-recognition"),
|
||||
]
|
||||
|
||||
# Get all models
|
||||
_get_model(object_model, "object-detection")
|
||||
_get_model(classification_model, "image-classification")
|
||||
_get_model(clip_image_model)
|
||||
_get_model(clip_text_model)
|
||||
_get_model(facial_recognition_model, "facial-recognition")
|
||||
for model_name, model_type in models:
|
||||
if eager_startup:
|
||||
get_cached_model(model_name, model_type)
|
||||
else:
|
||||
_get_model(model_name, model_type)
|
||||
|
||||
|
||||
@app.get("/")
|
||||
@@ -56,37 +68,30 @@ def ping():
|
||||
return "pong"
|
||||
|
||||
|
||||
@app.post("/object-detection/detect-object", status_code=200)
|
||||
def object_detection(payload: MlRequestBody):
|
||||
model = _get_model(object_model, "object-detection")
|
||||
assetPath = payload.thumbnailPath
|
||||
return run_engine(model, assetPath)
|
||||
|
||||
|
||||
@app.post("/image-classifier/tag-image", status_code=200)
|
||||
def image_classification(payload: MlRequestBody):
|
||||
model = _get_model(classification_model, "image-classification")
|
||||
model = get_cached_model(classification_model, "image-classification")
|
||||
assetPath = payload.thumbnailPath
|
||||
return run_engine(model, assetPath)
|
||||
|
||||
|
||||
@app.post("/sentence-transformer/encode-image", status_code=200)
|
||||
def clip_encode_image(payload: MlRequestBody):
|
||||
model = _get_model(clip_image_model)
|
||||
model = get_cached_model(clip_image_model, "clip")
|
||||
assetPath = payload.thumbnailPath
|
||||
return model.encode(Image.open(assetPath)).tolist()
|
||||
|
||||
|
||||
@app.post("/sentence-transformer/encode-text", status_code=200)
|
||||
def clip_encode_text(payload: ClipRequestBody):
|
||||
model = _get_model(clip_text_model)
|
||||
model = get_cached_model(clip_text_model, "clip")
|
||||
text = payload.text
|
||||
return model.encode(text).tolist()
|
||||
|
||||
|
||||
@app.post("/facial-recognition/detect-faces", status_code=200)
|
||||
def facial_recognition(payload: MlRequestBody):
|
||||
model = _get_model(facial_recognition_model, "facial-recognition")
|
||||
model = get_cached_model(facial_recognition_model, "facial-recognition")
|
||||
assetPath = payload.thumbnailPath
|
||||
img = cv.imread(assetPath)
|
||||
height, width, _ = img.shape
|
||||
@@ -94,7 +99,7 @@ def facial_recognition(payload: MlRequestBody):
|
||||
faces = model.get(img)
|
||||
|
||||
for face in faces:
|
||||
if face.det_score < 0.7:
|
||||
if face.det_score < min_face_score:
|
||||
continue
|
||||
x1, y1, x2, y2 = face.bbox
|
||||
|
||||
@@ -121,7 +126,7 @@ def run_engine(engine, path):
|
||||
|
||||
for index, pred in enumerate(predictions):
|
||||
tags = pred["label"].split(", ")
|
||||
if pred["score"] > 0.9:
|
||||
if pred["score"] > min_tag_score:
|
||||
result = [*result, *tags]
|
||||
|
||||
if len(result) > 1:
|
||||
@@ -130,26 +135,32 @@ def run_engine(engine, path):
|
||||
return result
|
||||
|
||||
|
||||
def _get_model(model, task=None):
|
||||
def get_cached_model(model, task):
|
||||
global _model_cache
|
||||
key = "|".join([model, str(task)])
|
||||
if key not in _model_cache:
|
||||
if task:
|
||||
if task == "facial-recognition":
|
||||
face_model = FaceAnalysis(
|
||||
name=model,
|
||||
root=cache_folder,
|
||||
allowed_modules=["detection", "recognition"],
|
||||
)
|
||||
face_model.prepare(ctx_id=0, det_size=(640, 640))
|
||||
_model_cache[key] = face_model
|
||||
else:
|
||||
_model_cache[key] = pipeline(model=model, task=task)
|
||||
else:
|
||||
_model_cache[key] = SentenceTransformer(model, cache_folder=cache_folder)
|
||||
model = _get_model(model, task)
|
||||
_model_cache[key] = model
|
||||
|
||||
return _model_cache[key]
|
||||
|
||||
|
||||
def _get_model(model, task):
|
||||
match task:
|
||||
case "facial-recognition":
|
||||
model = FaceAnalysis(
|
||||
name=model,
|
||||
root=cache_folder,
|
||||
allowed_modules=["detection", "recognition"],
|
||||
)
|
||||
model.prepare(ctx_id=0, det_size=(640, 640))
|
||||
case "clip":
|
||||
model = SentenceTransformer(model, cache_folder=cache_folder)
|
||||
case _:
|
||||
model = pipeline(model=model, task=task)
|
||||
return model
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
host = os.getenv("MACHINE_LEARNING_HOST", "0.0.0.0")
|
||||
port = int(os.getenv("MACHINE_LEARNING_PORT", 3003))
|
||||
|
||||
@@ -35,8 +35,8 @@ platform :android do
|
||||
task: 'bundle',
|
||||
build_type: 'Release',
|
||||
properties: {
|
||||
"android.injected.version.code" => 82,
|
||||
"android.injected.version.name" => "1.59.0",
|
||||
"android.injected.version.code" => 83,
|
||||
"android.injected.version.name" => "1.60.0",
|
||||
}
|
||||
)
|
||||
upload_to_play_store(skip_upload_apk: true, skip_upload_images: true, skip_upload_screenshots: true, aab: '../build/app/outputs/bundle/release/app-release.aab')
|
||||
|
||||
@@ -19,7 +19,7 @@ platform :ios do
|
||||
desc "iOS Beta"
|
||||
lane :beta do
|
||||
increment_version_number(
|
||||
version_number: "1.59.0"
|
||||
version_number: "1.60.0"
|
||||
)
|
||||
increment_build_number(
|
||||
build_number: latest_testflight_build_number + 1,
|
||||
|
||||
12
mobile/openapi/.openapi-generator/FILES
generated
12
mobile/openapi/.openapi-generator/FILES
generated
@@ -25,6 +25,8 @@ doc/AssetCountByTimeBucket.md
|
||||
doc/AssetCountByTimeBucketResponseDto.md
|
||||
doc/AssetCountByUserIdResponseDto.md
|
||||
doc/AssetFileUploadResponseDto.md
|
||||
doc/AssetIdsDto.md
|
||||
doc/AssetIdsResponseDto.md
|
||||
doc/AssetResponseDto.md
|
||||
doc/AssetTypeEnum.md
|
||||
doc/AuthDeviceResponseDto.md
|
||||
@@ -55,6 +57,7 @@ doc/JobCommand.md
|
||||
doc/JobCommandDto.md
|
||||
doc/JobCountsDto.md
|
||||
doc/JobName.md
|
||||
doc/JobSettingsDto.md
|
||||
doc/JobStatusDto.md
|
||||
doc/LoginCredentialDto.md
|
||||
doc/LoginResponseDto.md
|
||||
@@ -93,6 +96,7 @@ doc/SmartInfoResponseDto.md
|
||||
doc/SystemConfigApi.md
|
||||
doc/SystemConfigDto.md
|
||||
doc/SystemConfigFFmpegDto.md
|
||||
doc/SystemConfigJobDto.md
|
||||
doc/SystemConfigOAuthDto.md
|
||||
doc/SystemConfigPasswordLoginDto.md
|
||||
doc/SystemConfigStorageTemplateDto.md
|
||||
@@ -154,6 +158,8 @@ lib/model/asset_count_by_time_bucket.dart
|
||||
lib/model/asset_count_by_time_bucket_response_dto.dart
|
||||
lib/model/asset_count_by_user_id_response_dto.dart
|
||||
lib/model/asset_file_upload_response_dto.dart
|
||||
lib/model/asset_ids_dto.dart
|
||||
lib/model/asset_ids_response_dto.dart
|
||||
lib/model/asset_response_dto.dart
|
||||
lib/model/asset_type_enum.dart
|
||||
lib/model/auth_device_response_dto.dart
|
||||
@@ -182,6 +188,7 @@ lib/model/job_command.dart
|
||||
lib/model/job_command_dto.dart
|
||||
lib/model/job_counts_dto.dart
|
||||
lib/model/job_name.dart
|
||||
lib/model/job_settings_dto.dart
|
||||
lib/model/job_status_dto.dart
|
||||
lib/model/login_credential_dto.dart
|
||||
lib/model/login_response_dto.dart
|
||||
@@ -213,6 +220,7 @@ lib/model/sign_up_dto.dart
|
||||
lib/model/smart_info_response_dto.dart
|
||||
lib/model/system_config_dto.dart
|
||||
lib/model/system_config_f_fmpeg_dto.dart
|
||||
lib/model/system_config_job_dto.dart
|
||||
lib/model/system_config_o_auth_dto.dart
|
||||
lib/model/system_config_password_login_dto.dart
|
||||
lib/model/system_config_storage_template_dto.dart
|
||||
@@ -252,6 +260,8 @@ test/asset_count_by_time_bucket_response_dto_test.dart
|
||||
test/asset_count_by_time_bucket_test.dart
|
||||
test/asset_count_by_user_id_response_dto_test.dart
|
||||
test/asset_file_upload_response_dto_test.dart
|
||||
test/asset_ids_dto_test.dart
|
||||
test/asset_ids_response_dto_test.dart
|
||||
test/asset_response_dto_test.dart
|
||||
test/asset_type_enum_test.dart
|
||||
test/auth_device_response_dto_test.dart
|
||||
@@ -282,6 +292,7 @@ test/job_command_dto_test.dart
|
||||
test/job_command_test.dart
|
||||
test/job_counts_dto_test.dart
|
||||
test/job_name_test.dart
|
||||
test/job_settings_dto_test.dart
|
||||
test/job_status_dto_test.dart
|
||||
test/login_credential_dto_test.dart
|
||||
test/login_response_dto_test.dart
|
||||
@@ -320,6 +331,7 @@ test/smart_info_response_dto_test.dart
|
||||
test/system_config_api_test.dart
|
||||
test/system_config_dto_test.dart
|
||||
test/system_config_f_fmpeg_dto_test.dart
|
||||
test/system_config_job_dto_test.dart
|
||||
test/system_config_o_auth_dto_test.dart
|
||||
test/system_config_password_login_dto_test.dart
|
||||
test/system_config_storage_template_dto_test.dart
|
||||
|
||||
31
mobile/openapi/README.md
generated
31
mobile/openapi/README.md
generated
@@ -3,7 +3,7 @@ Immich API
|
||||
|
||||
This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:
|
||||
|
||||
- API version: 1.59.0
|
||||
- API version: 1.60.0
|
||||
- Build package: org.openapitools.codegen.languages.DartClientCodegen
|
||||
|
||||
## Requirements
|
||||
@@ -95,25 +95,25 @@ Class | Method | HTTP request | Description
|
||||
*AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist |
|
||||
*AssetApi* | [**createAssetsSharedLink**](doc//AssetApi.md#createassetssharedlink) | **POST** /asset/shared-link |
|
||||
*AssetApi* | [**deleteAsset**](doc//AssetApi.md#deleteasset) | **DELETE** /asset |
|
||||
*AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **GET** /asset/download/{assetId} |
|
||||
*AssetApi* | [**downloadFile**](doc//AssetApi.md#downloadfile) | **GET** /asset/download/{id} |
|
||||
*AssetApi* | [**downloadFiles**](doc//AssetApi.md#downloadfiles) | **POST** /asset/download-files |
|
||||
*AssetApi* | [**downloadLibrary**](doc//AssetApi.md#downloadlibrary) | **GET** /asset/download-library |
|
||||
*AssetApi* | [**getAllAssets**](doc//AssetApi.md#getallassets) | **GET** /asset |
|
||||
*AssetApi* | [**getArchivedAssetCountByUserId**](doc//AssetApi.md#getarchivedassetcountbyuserid) | **GET** /asset/stat/archive |
|
||||
*AssetApi* | [**getAssetById**](doc//AssetApi.md#getassetbyid) | **GET** /asset/assetById/{assetId} |
|
||||
*AssetApi* | [**getAssetById**](doc//AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} |
|
||||
*AssetApi* | [**getAssetByTimeBucket**](doc//AssetApi.md#getassetbytimebucket) | **POST** /asset/time-bucket |
|
||||
*AssetApi* | [**getAssetCountByTimeBucket**](doc//AssetApi.md#getassetcountbytimebucket) | **POST** /asset/count-by-time-bucket |
|
||||
*AssetApi* | [**getAssetCountByUserId**](doc//AssetApi.md#getassetcountbyuserid) | **GET** /asset/count-by-user-id |
|
||||
*AssetApi* | [**getAssetSearchTerms**](doc//AssetApi.md#getassetsearchterms) | **GET** /asset/search-terms |
|
||||
*AssetApi* | [**getAssetThumbnail**](doc//AssetApi.md#getassetthumbnail) | **GET** /asset/thumbnail/{assetId} |
|
||||
*AssetApi* | [**getAssetThumbnail**](doc//AssetApi.md#getassetthumbnail) | **GET** /asset/thumbnail/{id} |
|
||||
*AssetApi* | [**getCuratedLocations**](doc//AssetApi.md#getcuratedlocations) | **GET** /asset/curated-locations |
|
||||
*AssetApi* | [**getCuratedObjects**](doc//AssetApi.md#getcuratedobjects) | **GET** /asset/curated-objects |
|
||||
*AssetApi* | [**getMapMarkers**](doc//AssetApi.md#getmapmarkers) | **GET** /asset/map-marker |
|
||||
*AssetApi* | [**getUserAssetsByDeviceId**](doc//AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |
|
||||
*AssetApi* | [**removeAssetsFromSharedLink**](doc//AssetApi.md#removeassetsfromsharedlink) | **PATCH** /asset/shared-link/remove |
|
||||
*AssetApi* | [**searchAsset**](doc//AssetApi.md#searchasset) | **POST** /asset/search |
|
||||
*AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{assetId} |
|
||||
*AssetApi* | [**updateAsset**](doc//AssetApi.md#updateasset) | **PUT** /asset/{assetId} |
|
||||
*AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{id} |
|
||||
*AssetApi* | [**updateAsset**](doc//AssetApi.md#updateasset) | **PUT** /asset/{id} |
|
||||
*AssetApi* | [**uploadFile**](doc//AssetApi.md#uploadfile) | **POST** /asset/upload |
|
||||
*AuthenticationApi* | [**adminSignUp**](doc//AuthenticationApi.md#adminsignup) | **POST** /auth/admin-sign-up |
|
||||
*AuthenticationApi* | [**changePassword**](doc//AuthenticationApi.md#changepassword) | **POST** /auth/change-password |
|
||||
@@ -145,20 +145,23 @@ Class | Method | HTTP request | Description
|
||||
*ServerInfoApi* | [**getServerVersion**](doc//ServerInfoApi.md#getserverversion) | **GET** /server-info/version |
|
||||
*ServerInfoApi* | [**getStats**](doc//ServerInfoApi.md#getstats) | **GET** /server-info/stats |
|
||||
*ServerInfoApi* | [**pingServer**](doc//ServerInfoApi.md#pingserver) | **GET** /server-info/ping |
|
||||
*ShareApi* | [**editSharedLink**](doc//ShareApi.md#editsharedlink) | **PATCH** /share/{id} |
|
||||
*ShareApi* | [**getAllSharedLinks**](doc//ShareApi.md#getallsharedlinks) | **GET** /share |
|
||||
*ShareApi* | [**getMySharedLink**](doc//ShareApi.md#getmysharedlink) | **GET** /share/me |
|
||||
*ShareApi* | [**getSharedLinkById**](doc//ShareApi.md#getsharedlinkbyid) | **GET** /share/{id} |
|
||||
*ShareApi* | [**removeSharedLink**](doc//ShareApi.md#removesharedlink) | **DELETE** /share/{id} |
|
||||
*ShareApi* | [**updateSharedLink**](doc//ShareApi.md#updatesharedlink) | **PATCH** /share/{id} |
|
||||
*SystemConfigApi* | [**getConfig**](doc//SystemConfigApi.md#getconfig) | **GET** /system-config |
|
||||
*SystemConfigApi* | [**getDefaults**](doc//SystemConfigApi.md#getdefaults) | **GET** /system-config/defaults |
|
||||
*SystemConfigApi* | [**getStorageTemplateOptions**](doc//SystemConfigApi.md#getstoragetemplateoptions) | **GET** /system-config/storage-template-options |
|
||||
*SystemConfigApi* | [**updateConfig**](doc//SystemConfigApi.md#updateconfig) | **PUT** /system-config |
|
||||
*TagApi* | [**create**](doc//TagApi.md#create) | **POST** /tag |
|
||||
*TagApi* | [**delete**](doc//TagApi.md#delete) | **DELETE** /tag/{id} |
|
||||
*TagApi* | [**findAll**](doc//TagApi.md#findall) | **GET** /tag |
|
||||
*TagApi* | [**findOne**](doc//TagApi.md#findone) | **GET** /tag/{id} |
|
||||
*TagApi* | [**update**](doc//TagApi.md#update) | **PATCH** /tag/{id} |
|
||||
*TagApi* | [**createTag**](doc//TagApi.md#createtag) | **POST** /tag |
|
||||
*TagApi* | [**deleteTag**](doc//TagApi.md#deletetag) | **DELETE** /tag/{id} |
|
||||
*TagApi* | [**getAllTags**](doc//TagApi.md#getalltags) | **GET** /tag |
|
||||
*TagApi* | [**getTagAssets**](doc//TagApi.md#gettagassets) | **GET** /tag/{id}/assets |
|
||||
*TagApi* | [**getTagById**](doc//TagApi.md#gettagbyid) | **GET** /tag/{id} |
|
||||
*TagApi* | [**tagAssets**](doc//TagApi.md#tagassets) | **PUT** /tag/{id}/assets |
|
||||
*TagApi* | [**untagAssets**](doc//TagApi.md#untagassets) | **DELETE** /tag/{id}/assets |
|
||||
*TagApi* | [**updateTag**](doc//TagApi.md#updatetag) | **PATCH** /tag/{id} |
|
||||
*UserApi* | [**createProfileImage**](doc//UserApi.md#createprofileimage) | **POST** /user/profile-image |
|
||||
*UserApi* | [**createUser**](doc//UserApi.md#createuser) | **POST** /user |
|
||||
*UserApi* | [**deleteUser**](doc//UserApi.md#deleteuser) | **DELETE** /user/{userId} |
|
||||
@@ -192,6 +195,8 @@ Class | Method | HTTP request | Description
|
||||
- [AssetCountByTimeBucketResponseDto](doc//AssetCountByTimeBucketResponseDto.md)
|
||||
- [AssetCountByUserIdResponseDto](doc//AssetCountByUserIdResponseDto.md)
|
||||
- [AssetFileUploadResponseDto](doc//AssetFileUploadResponseDto.md)
|
||||
- [AssetIdsDto](doc//AssetIdsDto.md)
|
||||
- [AssetIdsResponseDto](doc//AssetIdsResponseDto.md)
|
||||
- [AssetResponseDto](doc//AssetResponseDto.md)
|
||||
- [AssetTypeEnum](doc//AssetTypeEnum.md)
|
||||
- [AuthDeviceResponseDto](doc//AuthDeviceResponseDto.md)
|
||||
@@ -220,6 +225,7 @@ Class | Method | HTTP request | Description
|
||||
- [JobCommandDto](doc//JobCommandDto.md)
|
||||
- [JobCountsDto](doc//JobCountsDto.md)
|
||||
- [JobName](doc//JobName.md)
|
||||
- [JobSettingsDto](doc//JobSettingsDto.md)
|
||||
- [JobStatusDto](doc//JobStatusDto.md)
|
||||
- [LoginCredentialDto](doc//LoginCredentialDto.md)
|
||||
- [LoginResponseDto](doc//LoginResponseDto.md)
|
||||
@@ -251,6 +257,7 @@ Class | Method | HTTP request | Description
|
||||
- [SmartInfoResponseDto](doc//SmartInfoResponseDto.md)
|
||||
- [SystemConfigDto](doc//SystemConfigDto.md)
|
||||
- [SystemConfigFFmpegDto](doc//SystemConfigFFmpegDto.md)
|
||||
- [SystemConfigJobDto](doc//SystemConfigJobDto.md)
|
||||
- [SystemConfigOAuthDto](doc//SystemConfigOAuthDto.md)
|
||||
- [SystemConfigPasswordLoginDto](doc//SystemConfigPasswordLoginDto.md)
|
||||
- [SystemConfigStorageTemplateDto](doc//SystemConfigStorageTemplateDto.md)
|
||||
|
||||
20
mobile/openapi/doc/AllJobStatusResponseDto.md
generated
20
mobile/openapi/doc/AllJobStatusResponseDto.md
generated
@@ -8,16 +8,16 @@ import 'package:openapi/api.dart';
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**thumbnailGenerationQueue** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**metadataExtractionQueue** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**videoConversionQueue** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**objectTaggingQueue** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**clipEncodingQueue** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**storageTemplateMigrationQueue** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**backgroundTaskQueue** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**searchQueue** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**recognizeFacesQueue** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**sidecarQueue** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**thumbnailGeneration** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**metadataExtraction** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**videoConversion** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**objectTagging** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**clipEncoding** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**storageTemplateMigration** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**backgroundTask** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**search** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**recognizeFaces** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
**sidecar** | [**JobStatusDto**](JobStatusDto.md) | |
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
56
mobile/openapi/doc/AssetApi.md
generated
56
mobile/openapi/doc/AssetApi.md
generated
@@ -15,25 +15,25 @@ Method | HTTP request | Description
|
||||
[**checkExistingAssets**](AssetApi.md#checkexistingassets) | **POST** /asset/exist |
|
||||
[**createAssetsSharedLink**](AssetApi.md#createassetssharedlink) | **POST** /asset/shared-link |
|
||||
[**deleteAsset**](AssetApi.md#deleteasset) | **DELETE** /asset |
|
||||
[**downloadFile**](AssetApi.md#downloadfile) | **GET** /asset/download/{assetId} |
|
||||
[**downloadFile**](AssetApi.md#downloadfile) | **GET** /asset/download/{id} |
|
||||
[**downloadFiles**](AssetApi.md#downloadfiles) | **POST** /asset/download-files |
|
||||
[**downloadLibrary**](AssetApi.md#downloadlibrary) | **GET** /asset/download-library |
|
||||
[**getAllAssets**](AssetApi.md#getallassets) | **GET** /asset |
|
||||
[**getArchivedAssetCountByUserId**](AssetApi.md#getarchivedassetcountbyuserid) | **GET** /asset/stat/archive |
|
||||
[**getAssetById**](AssetApi.md#getassetbyid) | **GET** /asset/assetById/{assetId} |
|
||||
[**getAssetById**](AssetApi.md#getassetbyid) | **GET** /asset/assetById/{id} |
|
||||
[**getAssetByTimeBucket**](AssetApi.md#getassetbytimebucket) | **POST** /asset/time-bucket |
|
||||
[**getAssetCountByTimeBucket**](AssetApi.md#getassetcountbytimebucket) | **POST** /asset/count-by-time-bucket |
|
||||
[**getAssetCountByUserId**](AssetApi.md#getassetcountbyuserid) | **GET** /asset/count-by-user-id |
|
||||
[**getAssetSearchTerms**](AssetApi.md#getassetsearchterms) | **GET** /asset/search-terms |
|
||||
[**getAssetThumbnail**](AssetApi.md#getassetthumbnail) | **GET** /asset/thumbnail/{assetId} |
|
||||
[**getAssetThumbnail**](AssetApi.md#getassetthumbnail) | **GET** /asset/thumbnail/{id} |
|
||||
[**getCuratedLocations**](AssetApi.md#getcuratedlocations) | **GET** /asset/curated-locations |
|
||||
[**getCuratedObjects**](AssetApi.md#getcuratedobjects) | **GET** /asset/curated-objects |
|
||||
[**getMapMarkers**](AssetApi.md#getmapmarkers) | **GET** /asset/map-marker |
|
||||
[**getUserAssetsByDeviceId**](AssetApi.md#getuserassetsbydeviceid) | **GET** /asset/{deviceId} |
|
||||
[**removeAssetsFromSharedLink**](AssetApi.md#removeassetsfromsharedlink) | **PATCH** /asset/shared-link/remove |
|
||||
[**searchAsset**](AssetApi.md#searchasset) | **POST** /asset/search |
|
||||
[**serveFile**](AssetApi.md#servefile) | **GET** /asset/file/{assetId} |
|
||||
[**updateAsset**](AssetApi.md#updateasset) | **PUT** /asset/{assetId} |
|
||||
[**serveFile**](AssetApi.md#servefile) | **GET** /asset/file/{id} |
|
||||
[**updateAsset**](AssetApi.md#updateasset) | **PUT** /asset/{id} |
|
||||
[**uploadFile**](AssetApi.md#uploadfile) | **POST** /asset/upload |
|
||||
|
||||
|
||||
@@ -378,7 +378,7 @@ Name | Type | Description | Notes
|
||||
[[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)
|
||||
|
||||
# **downloadFile**
|
||||
> MultipartFile downloadFile(assetId, key)
|
||||
> MultipartFile downloadFile(id, key)
|
||||
|
||||
|
||||
|
||||
@@ -401,11 +401,11 @@ import 'package:openapi/api.dart';
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = AssetApi();
|
||||
final assetId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final key = key_example; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.downloadFile(assetId, key);
|
||||
final result = api_instance.downloadFile(id, key);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling AssetApi->downloadFile: $e\n');
|
||||
@@ -416,7 +416,7 @@ try {
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**assetId** | **String**| |
|
||||
**id** | **String**| |
|
||||
**key** | **String**| | [optional]
|
||||
|
||||
### Return type
|
||||
@@ -553,7 +553,7 @@ Name | Type | Description | Notes
|
||||
[[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)
|
||||
|
||||
# **getAllAssets**
|
||||
> List<AssetResponseDto> getAllAssets(userId, isFavorite, isArchived, skip, ifNoneMatch)
|
||||
> List<AssetResponseDto> getAllAssets(userId, isFavorite, isArchived, withoutThumbs, skip, ifNoneMatch)
|
||||
|
||||
|
||||
|
||||
@@ -581,11 +581,12 @@ final api_instance = AssetApi();
|
||||
final userId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final isFavorite = true; // bool |
|
||||
final isArchived = true; // bool |
|
||||
final withoutThumbs = true; // bool | Include assets without thumbnails
|
||||
final skip = 8.14; // num |
|
||||
final ifNoneMatch = ifNoneMatch_example; // String | ETag of data already cached on the client
|
||||
|
||||
try {
|
||||
final result = api_instance.getAllAssets(userId, isFavorite, isArchived, skip, ifNoneMatch);
|
||||
final result = api_instance.getAllAssets(userId, isFavorite, isArchived, withoutThumbs, skip, ifNoneMatch);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling AssetApi->getAllAssets: $e\n');
|
||||
@@ -599,6 +600,7 @@ Name | Type | Description | Notes
|
||||
**userId** | **String**| | [optional]
|
||||
**isFavorite** | **bool**| | [optional]
|
||||
**isArchived** | **bool**| | [optional]
|
||||
**withoutThumbs** | **bool**| Include assets without thumbnails | [optional]
|
||||
**skip** | **num**| | [optional]
|
||||
**ifNoneMatch** | **String**| ETag of data already cached on the client | [optional]
|
||||
|
||||
@@ -669,7 +671,7 @@ This endpoint does not need any parameter.
|
||||
[[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)
|
||||
|
||||
# **getAssetById**
|
||||
> AssetResponseDto getAssetById(assetId, key)
|
||||
> AssetResponseDto getAssetById(id, key)
|
||||
|
||||
|
||||
|
||||
@@ -694,11 +696,11 @@ import 'package:openapi/api.dart';
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = AssetApi();
|
||||
final assetId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final key = key_example; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.getAssetById(assetId, key);
|
||||
final result = api_instance.getAssetById(id, key);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling AssetApi->getAssetById: $e\n');
|
||||
@@ -709,7 +711,7 @@ try {
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**assetId** | **String**| |
|
||||
**id** | **String**| |
|
||||
**key** | **String**| | [optional]
|
||||
|
||||
### Return type
|
||||
@@ -940,7 +942,7 @@ This endpoint does not need any parameter.
|
||||
[[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)
|
||||
|
||||
# **getAssetThumbnail**
|
||||
> MultipartFile getAssetThumbnail(assetId, format, key)
|
||||
> MultipartFile getAssetThumbnail(id, format, key)
|
||||
|
||||
|
||||
|
||||
@@ -963,12 +965,12 @@ import 'package:openapi/api.dart';
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = AssetApi();
|
||||
final assetId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final format = ; // ThumbnailFormat |
|
||||
final key = key_example; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.getAssetThumbnail(assetId, format, key);
|
||||
final result = api_instance.getAssetThumbnail(id, format, key);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling AssetApi->getAssetThumbnail: $e\n');
|
||||
@@ -979,7 +981,7 @@ try {
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**assetId** | **String**| |
|
||||
**id** | **String**| |
|
||||
**format** | [**ThumbnailFormat**](.md)| | [optional]
|
||||
**key** | **String**| | [optional]
|
||||
|
||||
@@ -1329,7 +1331,7 @@ Name | Type | Description | Notes
|
||||
[[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)
|
||||
|
||||
# **serveFile**
|
||||
> MultipartFile serveFile(assetId, isThumb, isWeb, key)
|
||||
> MultipartFile serveFile(id, isThumb, isWeb, key)
|
||||
|
||||
|
||||
|
||||
@@ -1352,13 +1354,13 @@ import 'package:openapi/api.dart';
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = AssetApi();
|
||||
final assetId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final isThumb = true; // bool |
|
||||
final isWeb = true; // bool |
|
||||
final key = key_example; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.serveFile(assetId, isThumb, isWeb, key);
|
||||
final result = api_instance.serveFile(id, isThumb, isWeb, key);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling AssetApi->serveFile: $e\n');
|
||||
@@ -1369,7 +1371,7 @@ try {
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**assetId** | **String**| |
|
||||
**id** | **String**| |
|
||||
**isThumb** | **bool**| | [optional]
|
||||
**isWeb** | **bool**| | [optional]
|
||||
**key** | **String**| | [optional]
|
||||
@@ -1390,7 +1392,7 @@ Name | Type | Description | Notes
|
||||
[[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)
|
||||
|
||||
# **updateAsset**
|
||||
> AssetResponseDto updateAsset(assetId, updateAssetDto)
|
||||
> AssetResponseDto updateAsset(id, updateAssetDto)
|
||||
|
||||
|
||||
|
||||
@@ -1415,11 +1417,11 @@ import 'package:openapi/api.dart';
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = AssetApi();
|
||||
final assetId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final updateAssetDto = UpdateAssetDto(); // UpdateAssetDto |
|
||||
|
||||
try {
|
||||
final result = api_instance.updateAsset(assetId, updateAssetDto);
|
||||
final result = api_instance.updateAsset(id, updateAssetDto);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling AssetApi->updateAsset: $e\n');
|
||||
@@ -1430,7 +1432,7 @@ try {
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**assetId** | **String**| |
|
||||
**id** | **String**| |
|
||||
**updateAssetDto** | [**UpdateAssetDto**](UpdateAssetDto.md)| |
|
||||
|
||||
### Return type
|
||||
|
||||
15
mobile/openapi/doc/AssetIdsDto.md
generated
Normal file
15
mobile/openapi/doc/AssetIdsDto.md
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
# openapi.model.AssetIdsDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**assetIds** | **List<String>** | | [default to const []]
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
17
mobile/openapi/doc/AssetIdsResponseDto.md
generated
Normal file
17
mobile/openapi/doc/AssetIdsResponseDto.md
generated
Normal file
@@ -0,0 +1,17 @@
|
||||
# openapi.model.AssetIdsResponseDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**assetId** | **String** | |
|
||||
**success** | **bool** | |
|
||||
**error** | **String** | | [optional]
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**timeGroup** | [**TimeGroupEnum**](TimeGroupEnum.md) | |
|
||||
**userId** | **String** | | [optional]
|
||||
**withoutThumbs** | **bool** | Include assets without thumbnails | [optional]
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
15
mobile/openapi/doc/JobSettingsDto.md
generated
Normal file
15
mobile/openapi/doc/JobSettingsDto.md
generated
Normal file
@@ -0,0 +1,15 @@
|
||||
# openapi.model.JobSettingsDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**concurrency** | **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)
|
||||
|
||||
|
||||
116
mobile/openapi/doc/ShareApi.md
generated
116
mobile/openapi/doc/ShareApi.md
generated
@@ -9,70 +9,13 @@ All URIs are relative to */api*
|
||||
|
||||
Method | HTTP request | Description
|
||||
------------- | ------------- | -------------
|
||||
[**editSharedLink**](ShareApi.md#editsharedlink) | **PATCH** /share/{id} |
|
||||
[**getAllSharedLinks**](ShareApi.md#getallsharedlinks) | **GET** /share |
|
||||
[**getMySharedLink**](ShareApi.md#getmysharedlink) | **GET** /share/me |
|
||||
[**getSharedLinkById**](ShareApi.md#getsharedlinkbyid) | **GET** /share/{id} |
|
||||
[**removeSharedLink**](ShareApi.md#removesharedlink) | **DELETE** /share/{id} |
|
||||
[**updateSharedLink**](ShareApi.md#updatesharedlink) | **PATCH** /share/{id} |
|
||||
|
||||
|
||||
# **editSharedLink**
|
||||
> SharedLinkResponseDto editSharedLink(id, editSharedLinkDto)
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = ShareApi();
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final editSharedLinkDto = EditSharedLinkDto(); // EditSharedLinkDto |
|
||||
|
||||
try {
|
||||
final result = api_instance.editSharedLink(id, editSharedLinkDto);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling ShareApi->editSharedLink: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**id** | **String**| |
|
||||
**editSharedLinkDto** | [**EditSharedLinkDto**](EditSharedLinkDto.md)| |
|
||||
|
||||
### Return type
|
||||
|
||||
[**SharedLinkResponseDto**](SharedLinkResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: application/json
|
||||
- **Accept**: application/json
|
||||
|
||||
[[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)
|
||||
|
||||
# **getAllSharedLinks**
|
||||
> List<SharedLinkResponseDto> getAllSharedLinks()
|
||||
|
||||
@@ -288,3 +231,60 @@ 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)
|
||||
|
||||
# **updateSharedLink**
|
||||
> SharedLinkResponseDto updateSharedLink(id, editSharedLinkDto)
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = ShareApi();
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final editSharedLinkDto = EditSharedLinkDto(); // EditSharedLinkDto |
|
||||
|
||||
try {
|
||||
final result = api_instance.updateSharedLink(id, editSharedLinkDto);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling ShareApi->updateSharedLink: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**id** | **String**| |
|
||||
**editSharedLinkDto** | [**EditSharedLinkDto**](EditSharedLinkDto.md)| |
|
||||
|
||||
### Return type
|
||||
|
||||
[**SharedLinkResponseDto**](SharedLinkResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: application/json
|
||||
- **Accept**: application/json
|
||||
|
||||
[[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)
|
||||
|
||||
|
||||
1
mobile/openapi/doc/SystemConfigDto.md
generated
1
mobile/openapi/doc/SystemConfigDto.md
generated
@@ -12,6 +12,7 @@ Name | Type | Description | Notes
|
||||
**oauth** | [**SystemConfigOAuthDto**](SystemConfigOAuthDto.md) | |
|
||||
**passwordLogin** | [**SystemConfigPasswordLoginDto**](SystemConfigPasswordLoginDto.md) | |
|
||||
**storageTemplate** | [**SystemConfigStorageTemplateDto**](SystemConfigStorageTemplateDto.md) | |
|
||||
**job** | [**SystemConfigJobDto**](SystemConfigJobDto.md) | |
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
24
mobile/openapi/doc/SystemConfigJobDto.md
generated
Normal file
24
mobile/openapi/doc/SystemConfigJobDto.md
generated
Normal file
@@ -0,0 +1,24 @@
|
||||
# openapi.model.SystemConfigJobDto
|
||||
|
||||
## Load the model package
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
```
|
||||
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**thumbnailGeneration** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
**metadataExtraction** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
**videoConversion** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
**objectTagging** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
**clipEncoding** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
**storageTemplateMigration** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
**backgroundTask** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
**search** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
**recognizeFaces** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
**sidecar** | [**JobSettingsDto**](JobSettingsDto.md) | |
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
222
mobile/openapi/doc/TagApi.md
generated
222
mobile/openapi/doc/TagApi.md
generated
@@ -9,15 +9,18 @@ All URIs are relative to */api*
|
||||
|
||||
Method | HTTP request | Description
|
||||
------------- | ------------- | -------------
|
||||
[**create**](TagApi.md#create) | **POST** /tag |
|
||||
[**delete**](TagApi.md#delete) | **DELETE** /tag/{id} |
|
||||
[**findAll**](TagApi.md#findall) | **GET** /tag |
|
||||
[**findOne**](TagApi.md#findone) | **GET** /tag/{id} |
|
||||
[**update**](TagApi.md#update) | **PATCH** /tag/{id} |
|
||||
[**createTag**](TagApi.md#createtag) | **POST** /tag |
|
||||
[**deleteTag**](TagApi.md#deletetag) | **DELETE** /tag/{id} |
|
||||
[**getAllTags**](TagApi.md#getalltags) | **GET** /tag |
|
||||
[**getTagAssets**](TagApi.md#gettagassets) | **GET** /tag/{id}/assets |
|
||||
[**getTagById**](TagApi.md#gettagbyid) | **GET** /tag/{id} |
|
||||
[**tagAssets**](TagApi.md#tagassets) | **PUT** /tag/{id}/assets |
|
||||
[**untagAssets**](TagApi.md#untagassets) | **DELETE** /tag/{id}/assets |
|
||||
[**updateTag**](TagApi.md#updatetag) | **PATCH** /tag/{id} |
|
||||
|
||||
|
||||
# **create**
|
||||
> TagResponseDto create(createTagDto)
|
||||
# **createTag**
|
||||
> TagResponseDto createTag(createTagDto)
|
||||
|
||||
|
||||
|
||||
@@ -43,10 +46,10 @@ final api_instance = TagApi();
|
||||
final createTagDto = CreateTagDto(); // CreateTagDto |
|
||||
|
||||
try {
|
||||
final result = api_instance.create(createTagDto);
|
||||
final result = api_instance.createTag(createTagDto);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling TagApi->create: $e\n');
|
||||
print('Exception when calling TagApi->createTag: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
@@ -71,8 +74,8 @@ Name | Type | Description | Notes
|
||||
|
||||
[[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)
|
||||
|
||||
# **delete**
|
||||
> delete(id)
|
||||
# **deleteTag**
|
||||
> deleteTag(id)
|
||||
|
||||
|
||||
|
||||
@@ -98,9 +101,9 @@ final api_instance = TagApi();
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
|
||||
try {
|
||||
api_instance.delete(id);
|
||||
api_instance.deleteTag(id);
|
||||
} catch (e) {
|
||||
print('Exception when calling TagApi->delete: $e\n');
|
||||
print('Exception when calling TagApi->deleteTag: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
@@ -125,8 +128,8 @@ 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)
|
||||
|
||||
# **findAll**
|
||||
> List<TagResponseDto> findAll()
|
||||
# **getAllTags**
|
||||
> List<TagResponseDto> getAllTags()
|
||||
|
||||
|
||||
|
||||
@@ -151,10 +154,10 @@ import 'package:openapi/api.dart';
|
||||
final api_instance = TagApi();
|
||||
|
||||
try {
|
||||
final result = api_instance.findAll();
|
||||
final result = api_instance.getAllTags();
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling TagApi->findAll: $e\n');
|
||||
print('Exception when calling TagApi->getAllTags: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
@@ -176,8 +179,8 @@ This endpoint does not need any parameter.
|
||||
|
||||
[[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)
|
||||
|
||||
# **findOne**
|
||||
> TagResponseDto findOne(id)
|
||||
# **getTagAssets**
|
||||
> List<AssetResponseDto> getTagAssets(id)
|
||||
|
||||
|
||||
|
||||
@@ -203,10 +206,65 @@ final api_instance = TagApi();
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.findOne(id);
|
||||
final result = api_instance.getTagAssets(id);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling TagApi->findOne: $e\n');
|
||||
print('Exception when calling TagApi->getTagAssets: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**id** | **String**| |
|
||||
|
||||
### Return type
|
||||
|
||||
[**List<AssetResponseDto>**](AssetResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: Not defined
|
||||
- **Accept**: application/json
|
||||
|
||||
[[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)
|
||||
|
||||
# **getTagById**
|
||||
> TagResponseDto getTagById(id)
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = TagApi();
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
|
||||
try {
|
||||
final result = api_instance.getTagById(id);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling TagApi->getTagById: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
@@ -231,8 +289,122 @@ Name | Type | Description | Notes
|
||||
|
||||
[[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)
|
||||
|
||||
# **update**
|
||||
> TagResponseDto update(id, updateTagDto)
|
||||
# **tagAssets**
|
||||
> List<AssetIdsResponseDto> tagAssets(id, assetIdsDto)
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = TagApi();
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final assetIdsDto = AssetIdsDto(); // AssetIdsDto |
|
||||
|
||||
try {
|
||||
final result = api_instance.tagAssets(id, assetIdsDto);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling TagApi->tagAssets: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**id** | **String**| |
|
||||
**assetIdsDto** | [**AssetIdsDto**](AssetIdsDto.md)| |
|
||||
|
||||
### Return type
|
||||
|
||||
[**List<AssetIdsResponseDto>**](AssetIdsResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: application/json
|
||||
- **Accept**: application/json
|
||||
|
||||
[[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)
|
||||
|
||||
# **untagAssets**
|
||||
> List<AssetIdsResponseDto> untagAssets(id, assetIdsDto)
|
||||
|
||||
|
||||
|
||||
### Example
|
||||
```dart
|
||||
import 'package:openapi/api.dart';
|
||||
// TODO Configure API key authorization: cookie
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('cookie').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure API key authorization: api_key
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKey = 'YOUR_API_KEY';
|
||||
// uncomment below to setup prefix (e.g. Bearer) for API key, if needed
|
||||
//defaultApiClient.getAuthentication<ApiKeyAuth>('api_key').apiKeyPrefix = 'Bearer';
|
||||
// TODO Configure HTTP Bearer authorization: bearer
|
||||
// Case 1. Use String Token
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken('YOUR_ACCESS_TOKEN');
|
||||
// Case 2. Use Function which generate token.
|
||||
// String yourTokenGeneratorFunction() { ... }
|
||||
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
|
||||
|
||||
final api_instance = TagApi();
|
||||
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final assetIdsDto = AssetIdsDto(); // AssetIdsDto |
|
||||
|
||||
try {
|
||||
final result = api_instance.untagAssets(id, assetIdsDto);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling TagApi->untagAssets: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
Name | Type | Description | Notes
|
||||
------------- | ------------- | ------------- | -------------
|
||||
**id** | **String**| |
|
||||
**assetIdsDto** | [**AssetIdsDto**](AssetIdsDto.md)| |
|
||||
|
||||
### Return type
|
||||
|
||||
[**List<AssetIdsResponseDto>**](AssetIdsResponseDto.md)
|
||||
|
||||
### Authorization
|
||||
|
||||
[cookie](../README.md#cookie), [api_key](../README.md#api_key), [bearer](../README.md#bearer)
|
||||
|
||||
### HTTP request headers
|
||||
|
||||
- **Content-Type**: application/json
|
||||
- **Accept**: application/json
|
||||
|
||||
[[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)
|
||||
|
||||
# **updateTag**
|
||||
> TagResponseDto updateTag(id, updateTagDto)
|
||||
|
||||
|
||||
|
||||
@@ -259,10 +431,10 @@ final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
|
||||
final updateTagDto = UpdateTagDto(); // UpdateTagDto |
|
||||
|
||||
try {
|
||||
final result = api_instance.update(id, updateTagDto);
|
||||
final result = api_instance.updateTag(id, updateTagDto);
|
||||
print(result);
|
||||
} catch (e) {
|
||||
print('Exception when calling TagApi->update: $e\n');
|
||||
print('Exception when calling TagApi->updateTag: $e\n');
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
3
mobile/openapi/doc/TagResponseDto.md
generated
3
mobile/openapi/doc/TagResponseDto.md
generated
@@ -8,11 +8,10 @@ import 'package:openapi/api.dart';
|
||||
## Properties
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**id** | **String** | |
|
||||
**type** | [**TagTypeEnum**](TagTypeEnum.md) | |
|
||||
**id** | **String** | |
|
||||
**name** | **String** | |
|
||||
**userId** | **String** | |
|
||||
**renameTagId** | **String** | | [optional]
|
||||
|
||||
[[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
mobile/openapi/doc/UpdateTagDto.md
generated
1
mobile/openapi/doc/UpdateTagDto.md
generated
@@ -9,7 +9,6 @@ import 'package:openapi/api.dart';
|
||||
Name | Type | Description | Notes
|
||||
------------ | ------------- | ------------- | -------------
|
||||
**name** | **String** | | [optional]
|
||||
**renameTagId** | **String** | | [optional]
|
||||
|
||||
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
|
||||
|
||||
|
||||
4
mobile/openapi/lib/api.dart
generated
4
mobile/openapi/lib/api.dart
generated
@@ -62,6 +62,8 @@ part 'model/asset_count_by_time_bucket.dart';
|
||||
part 'model/asset_count_by_time_bucket_response_dto.dart';
|
||||
part 'model/asset_count_by_user_id_response_dto.dart';
|
||||
part 'model/asset_file_upload_response_dto.dart';
|
||||
part 'model/asset_ids_dto.dart';
|
||||
part 'model/asset_ids_response_dto.dart';
|
||||
part 'model/asset_response_dto.dart';
|
||||
part 'model/asset_type_enum.dart';
|
||||
part 'model/auth_device_response_dto.dart';
|
||||
@@ -90,6 +92,7 @@ part 'model/job_command.dart';
|
||||
part 'model/job_command_dto.dart';
|
||||
part 'model/job_counts_dto.dart';
|
||||
part 'model/job_name.dart';
|
||||
part 'model/job_settings_dto.dart';
|
||||
part 'model/job_status_dto.dart';
|
||||
part 'model/login_credential_dto.dart';
|
||||
part 'model/login_response_dto.dart';
|
||||
@@ -121,6 +124,7 @@ part 'model/sign_up_dto.dart';
|
||||
part 'model/smart_info_response_dto.dart';
|
||||
part 'model/system_config_dto.dart';
|
||||
part 'model/system_config_f_fmpeg_dto.dart';
|
||||
part 'model/system_config_job_dto.dart';
|
||||
part 'model/system_config_o_auth_dto.dart';
|
||||
part 'model/system_config_password_login_dto.dart';
|
||||
part 'model/system_config_storage_template_dto.dart';
|
||||
|
||||
91
mobile/openapi/lib/api/asset_api.dart
generated
91
mobile/openapi/lib/api/asset_api.dart
generated
@@ -332,16 +332,16 @@ class AssetApi {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /asset/download/{assetId}' operation and returns the [Response].
|
||||
/// Performs an HTTP 'GET /asset/download/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] assetId (required):
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<Response> downloadFileWithHttpInfo(String assetId, { String? key, }) async {
|
||||
Future<Response> downloadFileWithHttpInfo(String id, { String? key, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/asset/download/{assetId}'
|
||||
.replaceAll('{assetId}', assetId);
|
||||
final path = r'/asset/download/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
@@ -370,11 +370,11 @@ class AssetApi {
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] assetId (required):
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<MultipartFile?> downloadFile(String assetId, { String? key, }) async {
|
||||
final response = await downloadFileWithHttpInfo(assetId, key: key, );
|
||||
Future<MultipartFile?> downloadFile(String id, { String? key, }) async {
|
||||
final response = await downloadFileWithHttpInfo(id, key: key, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
@@ -525,11 +525,14 @@ class AssetApi {
|
||||
///
|
||||
/// * [bool] isArchived:
|
||||
///
|
||||
/// * [bool] withoutThumbs:
|
||||
/// Include assets without thumbnails
|
||||
///
|
||||
/// * [num] skip:
|
||||
///
|
||||
/// * [String] ifNoneMatch:
|
||||
/// ETag of data already cached on the client
|
||||
Future<Response> getAllAssetsWithHttpInfo({ String? userId, bool? isFavorite, bool? isArchived, num? skip, String? ifNoneMatch, }) async {
|
||||
Future<Response> getAllAssetsWithHttpInfo({ String? userId, bool? isFavorite, bool? isArchived, bool? withoutThumbs, num? skip, String? ifNoneMatch, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/asset';
|
||||
|
||||
@@ -549,6 +552,9 @@ class AssetApi {
|
||||
if (isArchived != null) {
|
||||
queryParams.addAll(_queryParams('', 'isArchived', isArchived));
|
||||
}
|
||||
if (withoutThumbs != null) {
|
||||
queryParams.addAll(_queryParams('', 'withoutThumbs', withoutThumbs));
|
||||
}
|
||||
if (skip != null) {
|
||||
queryParams.addAll(_queryParams('', 'skip', skip));
|
||||
}
|
||||
@@ -581,12 +587,15 @@ class AssetApi {
|
||||
///
|
||||
/// * [bool] isArchived:
|
||||
///
|
||||
/// * [bool] withoutThumbs:
|
||||
/// Include assets without thumbnails
|
||||
///
|
||||
/// * [num] skip:
|
||||
///
|
||||
/// * [String] ifNoneMatch:
|
||||
/// ETag of data already cached on the client
|
||||
Future<List<AssetResponseDto>?> getAllAssets({ String? userId, bool? isFavorite, bool? isArchived, num? skip, String? ifNoneMatch, }) async {
|
||||
final response = await getAllAssetsWithHttpInfo( userId: userId, isFavorite: isFavorite, isArchived: isArchived, skip: skip, ifNoneMatch: ifNoneMatch, );
|
||||
Future<List<AssetResponseDto>?> getAllAssets({ String? userId, bool? isFavorite, bool? isArchived, bool? withoutThumbs, num? skip, String? ifNoneMatch, }) async {
|
||||
final response = await getAllAssetsWithHttpInfo( userId: userId, isFavorite: isFavorite, isArchived: isArchived, withoutThumbs: withoutThumbs, skip: skip, ifNoneMatch: ifNoneMatch, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
@@ -650,13 +659,13 @@ class AssetApi {
|
||||
///
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] assetId (required):
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<Response> getAssetByIdWithHttpInfo(String assetId, { String? key, }) async {
|
||||
Future<Response> getAssetByIdWithHttpInfo(String id, { String? key, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/asset/assetById/{assetId}'
|
||||
.replaceAll('{assetId}', assetId);
|
||||
final path = r'/asset/assetById/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
@@ -687,11 +696,11 @@ class AssetApi {
|
||||
///
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] assetId (required):
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<AssetResponseDto?> getAssetById(String assetId, { String? key, }) async {
|
||||
final response = await getAssetByIdWithHttpInfo(assetId, key: key, );
|
||||
Future<AssetResponseDto?> getAssetById(String id, { String? key, }) async {
|
||||
final response = await getAssetByIdWithHttpInfo(id, key: key, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
@@ -887,18 +896,18 @@ class AssetApi {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /asset/thumbnail/{assetId}' operation and returns the [Response].
|
||||
/// Performs an HTTP 'GET /asset/thumbnail/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] assetId (required):
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [ThumbnailFormat] format:
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<Response> getAssetThumbnailWithHttpInfo(String assetId, { ThumbnailFormat? format, String? key, }) async {
|
||||
Future<Response> getAssetThumbnailWithHttpInfo(String id, { ThumbnailFormat? format, String? key, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/asset/thumbnail/{assetId}'
|
||||
.replaceAll('{assetId}', assetId);
|
||||
final path = r'/asset/thumbnail/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
@@ -930,13 +939,13 @@ class AssetApi {
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] assetId (required):
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [ThumbnailFormat] format:
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<MultipartFile?> getAssetThumbnail(String assetId, { ThumbnailFormat? format, String? key, }) async {
|
||||
final response = await getAssetThumbnailWithHttpInfo(assetId, format: format, key: key, );
|
||||
Future<MultipartFile?> getAssetThumbnail(String id, { ThumbnailFormat? format, String? key, }) async {
|
||||
final response = await getAssetThumbnailWithHttpInfo(id, format: format, key: key, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
@@ -1267,20 +1276,20 @@ class AssetApi {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /asset/file/{assetId}' operation and returns the [Response].
|
||||
/// Performs an HTTP 'GET /asset/file/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] assetId (required):
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [bool] isThumb:
|
||||
///
|
||||
/// * [bool] isWeb:
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<Response> serveFileWithHttpInfo(String assetId, { bool? isThumb, bool? isWeb, String? key, }) async {
|
||||
Future<Response> serveFileWithHttpInfo(String id, { bool? isThumb, bool? isWeb, String? key, }) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/asset/file/{assetId}'
|
||||
.replaceAll('{assetId}', assetId);
|
||||
final path = r'/asset/file/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
@@ -1315,15 +1324,15 @@ class AssetApi {
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] assetId (required):
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [bool] isThumb:
|
||||
///
|
||||
/// * [bool] isWeb:
|
||||
///
|
||||
/// * [String] key:
|
||||
Future<MultipartFile?> serveFile(String assetId, { bool? isThumb, bool? isWeb, String? key, }) async {
|
||||
final response = await serveFileWithHttpInfo(assetId, isThumb: isThumb, isWeb: isWeb, key: key, );
|
||||
Future<MultipartFile?> serveFile(String id, { bool? isThumb, bool? isWeb, String? key, }) async {
|
||||
final response = await serveFileWithHttpInfo(id, isThumb: isThumb, isWeb: isWeb, key: key, );
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
@@ -1343,13 +1352,13 @@ class AssetApi {
|
||||
///
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] assetId (required):
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [UpdateAssetDto] updateAssetDto (required):
|
||||
Future<Response> updateAssetWithHttpInfo(String assetId, UpdateAssetDto updateAssetDto,) async {
|
||||
Future<Response> updateAssetWithHttpInfo(String id, UpdateAssetDto updateAssetDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/asset/{assetId}'
|
||||
.replaceAll('{assetId}', assetId);
|
||||
final path = r'/asset/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = updateAssetDto;
|
||||
@@ -1376,11 +1385,11 @@ class AssetApi {
|
||||
///
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] assetId (required):
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [UpdateAssetDto] updateAssetDto (required):
|
||||
Future<AssetResponseDto?> updateAsset(String assetId, UpdateAssetDto updateAssetDto,) async {
|
||||
final response = await updateAssetWithHttpInfo(assetId, updateAssetDto,);
|
||||
Future<AssetResponseDto?> updateAsset(String id, UpdateAssetDto updateAssetDto,) async {
|
||||
final response = await updateAssetWithHttpInfo(id, updateAssetDto,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
|
||||
104
mobile/openapi/lib/api/share_api.dart
generated
104
mobile/openapi/lib/api/share_api.dart
generated
@@ -16,58 +16,6 @@ class ShareApi {
|
||||
|
||||
final ApiClient apiClient;
|
||||
|
||||
/// Performs an HTTP 'PATCH /share/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [EditSharedLinkDto] editSharedLinkDto (required):
|
||||
Future<Response> editSharedLinkWithHttpInfo(String id, EditSharedLinkDto editSharedLinkDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/share/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = editSharedLinkDto;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'PATCH',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [EditSharedLinkDto] editSharedLinkDto (required):
|
||||
Future<SharedLinkResponseDto?> editSharedLink(String id, EditSharedLinkDto editSharedLinkDto,) async {
|
||||
final response = await editSharedLinkWithHttpInfo(id, editSharedLinkDto,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SharedLinkResponseDto',) as SharedLinkResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /share' operation and returns the [Response].
|
||||
Future<Response> getAllSharedLinksWithHttpInfo() async {
|
||||
// ignore: prefer_const_declarations
|
||||
@@ -250,4 +198,56 @@ class ShareApi {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'PATCH /share/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [EditSharedLinkDto] editSharedLinkDto (required):
|
||||
Future<Response> updateSharedLinkWithHttpInfo(String id, EditSharedLinkDto editSharedLinkDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/share/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = editSharedLinkDto;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'PATCH',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [EditSharedLinkDto] editSharedLinkDto (required):
|
||||
Future<SharedLinkResponseDto?> updateSharedLink(String id, EditSharedLinkDto editSharedLinkDto,) async {
|
||||
final response = await updateSharedLinkWithHttpInfo(id, editSharedLinkDto,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'SharedLinkResponseDto',) as SharedLinkResponseDto;
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
191
mobile/openapi/lib/api/tag_api.dart
generated
191
mobile/openapi/lib/api/tag_api.dart
generated
@@ -20,7 +20,7 @@ class TagApi {
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [CreateTagDto] createTagDto (required):
|
||||
Future<Response> createWithHttpInfo(CreateTagDto createTagDto,) async {
|
||||
Future<Response> createTagWithHttpInfo(CreateTagDto createTagDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/tag';
|
||||
|
||||
@@ -48,8 +48,8 @@ class TagApi {
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [CreateTagDto] createTagDto (required):
|
||||
Future<TagResponseDto?> create(CreateTagDto createTagDto,) async {
|
||||
final response = await createWithHttpInfo(createTagDto,);
|
||||
Future<TagResponseDto?> createTag(CreateTagDto createTagDto,) async {
|
||||
final response = await createTagWithHttpInfo(createTagDto,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
@@ -67,7 +67,7 @@ class TagApi {
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<Response> deleteWithHttpInfo(String id,) async {
|
||||
Future<Response> deleteTagWithHttpInfo(String id,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/tag/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
@@ -96,15 +96,15 @@ class TagApi {
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<void> delete(String id,) async {
|
||||
final response = await deleteWithHttpInfo(id,);
|
||||
Future<void> deleteTag(String id,) async {
|
||||
final response = await deleteTagWithHttpInfo(id,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /tag' operation and returns the [Response].
|
||||
Future<Response> findAllWithHttpInfo() async {
|
||||
Future<Response> getAllTagsWithHttpInfo() async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/tag';
|
||||
|
||||
@@ -129,8 +129,8 @@ class TagApi {
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<TagResponseDto>?> findAll() async {
|
||||
final response = await findAllWithHttpInfo();
|
||||
Future<List<TagResponseDto>?> getAllTags() async {
|
||||
final response = await getAllTagsWithHttpInfo();
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
@@ -147,11 +147,62 @@ class TagApi {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /tag/{id}/assets' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<Response> getTagAssetsWithHttpInfo(String id,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/tag/{id}/assets'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>[];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'GET',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<List<AssetResponseDto>?> getTagAssets(String id,) async {
|
||||
final response = await getTagAssetsWithHttpInfo(id,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
final responseBody = await _decodeBodyBytes(response);
|
||||
return (await apiClient.deserializeAsync(responseBody, 'List<AssetResponseDto>') as List)
|
||||
.cast<AssetResponseDto>()
|
||||
.toList();
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'GET /tag/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<Response> findOneWithHttpInfo(String id,) async {
|
||||
Future<Response> getTagByIdWithHttpInfo(String id,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/tag/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
@@ -180,8 +231,8 @@ class TagApi {
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
Future<TagResponseDto?> findOne(String id,) async {
|
||||
final response = await findOneWithHttpInfo(id,);
|
||||
Future<TagResponseDto?> getTagById(String id,) async {
|
||||
final response = await getTagByIdWithHttpInfo(id,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
@@ -195,13 +246,123 @@ class TagApi {
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'PUT /tag/{id}/assets' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [AssetIdsDto] assetIdsDto (required):
|
||||
Future<Response> tagAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/tag/{id}/assets'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = assetIdsDto;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'PUT',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [AssetIdsDto] assetIdsDto (required):
|
||||
Future<List<AssetIdsResponseDto>?> tagAssets(String id, AssetIdsDto assetIdsDto,) async {
|
||||
final response = await tagAssetsWithHttpInfo(id, assetIdsDto,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
final responseBody = await _decodeBodyBytes(response);
|
||||
return (await apiClient.deserializeAsync(responseBody, 'List<AssetIdsResponseDto>') as List)
|
||||
.cast<AssetIdsResponseDto>()
|
||||
.toList();
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'DELETE /tag/{id}/assets' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [AssetIdsDto] assetIdsDto (required):
|
||||
Future<Response> untagAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/tag/{id}/assets'
|
||||
.replaceAll('{id}', id);
|
||||
|
||||
// ignore: prefer_final_locals
|
||||
Object? postBody = assetIdsDto;
|
||||
|
||||
final queryParams = <QueryParam>[];
|
||||
final headerParams = <String, String>{};
|
||||
final formParams = <String, String>{};
|
||||
|
||||
const contentTypes = <String>['application/json'];
|
||||
|
||||
|
||||
return apiClient.invokeAPI(
|
||||
path,
|
||||
'DELETE',
|
||||
queryParams,
|
||||
postBody,
|
||||
headerParams,
|
||||
formParams,
|
||||
contentTypes.isEmpty ? null : contentTypes.first,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [AssetIdsDto] assetIdsDto (required):
|
||||
Future<List<AssetIdsResponseDto>?> untagAssets(String id, AssetIdsDto assetIdsDto,) async {
|
||||
final response = await untagAssetsWithHttpInfo(id, assetIdsDto,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||
// FormatException when trying to decode an empty string.
|
||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||
final responseBody = await _decodeBodyBytes(response);
|
||||
return (await apiClient.deserializeAsync(responseBody, 'List<AssetIdsResponseDto>') as List)
|
||||
.cast<AssetIdsResponseDto>()
|
||||
.toList();
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Performs an HTTP 'PATCH /tag/{id}' operation and returns the [Response].
|
||||
/// Parameters:
|
||||
///
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [UpdateTagDto] updateTagDto (required):
|
||||
Future<Response> updateWithHttpInfo(String id, UpdateTagDto updateTagDto,) async {
|
||||
Future<Response> updateTagWithHttpInfo(String id, UpdateTagDto updateTagDto,) async {
|
||||
// ignore: prefer_const_declarations
|
||||
final path = r'/tag/{id}'
|
||||
.replaceAll('{id}', id);
|
||||
@@ -232,8 +393,8 @@ class TagApi {
|
||||
/// * [String] id (required):
|
||||
///
|
||||
/// * [UpdateTagDto] updateTagDto (required):
|
||||
Future<TagResponseDto?> update(String id, UpdateTagDto updateTagDto,) async {
|
||||
final response = await updateWithHttpInfo(id, updateTagDto,);
|
||||
Future<TagResponseDto?> updateTag(String id, UpdateTagDto updateTagDto,) async {
|
||||
final response = await updateTagWithHttpInfo(id, updateTagDto,);
|
||||
if (response.statusCode >= HttpStatus.badRequest) {
|
||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||
}
|
||||
|
||||
8
mobile/openapi/lib/api_client.dart
generated
8
mobile/openapi/lib/api_client.dart
generated
@@ -219,6 +219,10 @@ class ApiClient {
|
||||
return AssetCountByUserIdResponseDto.fromJson(value);
|
||||
case 'AssetFileUploadResponseDto':
|
||||
return AssetFileUploadResponseDto.fromJson(value);
|
||||
case 'AssetIdsDto':
|
||||
return AssetIdsDto.fromJson(value);
|
||||
case 'AssetIdsResponseDto':
|
||||
return AssetIdsResponseDto.fromJson(value);
|
||||
case 'AssetResponseDto':
|
||||
return AssetResponseDto.fromJson(value);
|
||||
case 'AssetTypeEnum':
|
||||
@@ -275,6 +279,8 @@ class ApiClient {
|
||||
return JobCountsDto.fromJson(value);
|
||||
case 'JobName':
|
||||
return JobNameTypeTransformer().decode(value);
|
||||
case 'JobSettingsDto':
|
||||
return JobSettingsDto.fromJson(value);
|
||||
case 'JobStatusDto':
|
||||
return JobStatusDto.fromJson(value);
|
||||
case 'LoginCredentialDto':
|
||||
@@ -337,6 +343,8 @@ class ApiClient {
|
||||
return SystemConfigDto.fromJson(value);
|
||||
case 'SystemConfigFFmpegDto':
|
||||
return SystemConfigFFmpegDto.fromJson(value);
|
||||
case 'SystemConfigJobDto':
|
||||
return SystemConfigJobDto.fromJson(value);
|
||||
case 'SystemConfigOAuthDto':
|
||||
return SystemConfigOAuthDto.fromJson(value);
|
||||
case 'SystemConfigPasswordLoginDto':
|
||||
|
||||
@@ -13,80 +13,80 @@ part of openapi.api;
|
||||
class AllJobStatusResponseDto {
|
||||
/// Returns a new [AllJobStatusResponseDto] instance.
|
||||
AllJobStatusResponseDto({
|
||||
required this.thumbnailGenerationQueue,
|
||||
required this.metadataExtractionQueue,
|
||||
required this.videoConversionQueue,
|
||||
required this.objectTaggingQueue,
|
||||
required this.clipEncodingQueue,
|
||||
required this.storageTemplateMigrationQueue,
|
||||
required this.backgroundTaskQueue,
|
||||
required this.searchQueue,
|
||||
required this.recognizeFacesQueue,
|
||||
required this.sidecarQueue,
|
||||
required this.thumbnailGeneration,
|
||||
required this.metadataExtraction,
|
||||
required this.videoConversion,
|
||||
required this.objectTagging,
|
||||
required this.clipEncoding,
|
||||
required this.storageTemplateMigration,
|
||||
required this.backgroundTask,
|
||||
required this.search,
|
||||
required this.recognizeFaces,
|
||||
required this.sidecar,
|
||||
});
|
||||
|
||||
JobStatusDto thumbnailGenerationQueue;
|
||||
JobStatusDto thumbnailGeneration;
|
||||
|
||||
JobStatusDto metadataExtractionQueue;
|
||||
JobStatusDto metadataExtraction;
|
||||
|
||||
JobStatusDto videoConversionQueue;
|
||||
JobStatusDto videoConversion;
|
||||
|
||||
JobStatusDto objectTaggingQueue;
|
||||
JobStatusDto objectTagging;
|
||||
|
||||
JobStatusDto clipEncodingQueue;
|
||||
JobStatusDto clipEncoding;
|
||||
|
||||
JobStatusDto storageTemplateMigrationQueue;
|
||||
JobStatusDto storageTemplateMigration;
|
||||
|
||||
JobStatusDto backgroundTaskQueue;
|
||||
JobStatusDto backgroundTask;
|
||||
|
||||
JobStatusDto searchQueue;
|
||||
JobStatusDto search;
|
||||
|
||||
JobStatusDto recognizeFacesQueue;
|
||||
JobStatusDto recognizeFaces;
|
||||
|
||||
JobStatusDto sidecarQueue;
|
||||
JobStatusDto sidecar;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is AllJobStatusResponseDto &&
|
||||
other.thumbnailGenerationQueue == thumbnailGenerationQueue &&
|
||||
other.metadataExtractionQueue == metadataExtractionQueue &&
|
||||
other.videoConversionQueue == videoConversionQueue &&
|
||||
other.objectTaggingQueue == objectTaggingQueue &&
|
||||
other.clipEncodingQueue == clipEncodingQueue &&
|
||||
other.storageTemplateMigrationQueue == storageTemplateMigrationQueue &&
|
||||
other.backgroundTaskQueue == backgroundTaskQueue &&
|
||||
other.searchQueue == searchQueue &&
|
||||
other.recognizeFacesQueue == recognizeFacesQueue &&
|
||||
other.sidecarQueue == sidecarQueue;
|
||||
other.thumbnailGeneration == thumbnailGeneration &&
|
||||
other.metadataExtraction == metadataExtraction &&
|
||||
other.videoConversion == videoConversion &&
|
||||
other.objectTagging == objectTagging &&
|
||||
other.clipEncoding == clipEncoding &&
|
||||
other.storageTemplateMigration == storageTemplateMigration &&
|
||||
other.backgroundTask == backgroundTask &&
|
||||
other.search == search &&
|
||||
other.recognizeFaces == recognizeFaces &&
|
||||
other.sidecar == sidecar;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(thumbnailGenerationQueue.hashCode) +
|
||||
(metadataExtractionQueue.hashCode) +
|
||||
(videoConversionQueue.hashCode) +
|
||||
(objectTaggingQueue.hashCode) +
|
||||
(clipEncodingQueue.hashCode) +
|
||||
(storageTemplateMigrationQueue.hashCode) +
|
||||
(backgroundTaskQueue.hashCode) +
|
||||
(searchQueue.hashCode) +
|
||||
(recognizeFacesQueue.hashCode) +
|
||||
(sidecarQueue.hashCode);
|
||||
(thumbnailGeneration.hashCode) +
|
||||
(metadataExtraction.hashCode) +
|
||||
(videoConversion.hashCode) +
|
||||
(objectTagging.hashCode) +
|
||||
(clipEncoding.hashCode) +
|
||||
(storageTemplateMigration.hashCode) +
|
||||
(backgroundTask.hashCode) +
|
||||
(search.hashCode) +
|
||||
(recognizeFaces.hashCode) +
|
||||
(sidecar.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'AllJobStatusResponseDto[thumbnailGenerationQueue=$thumbnailGenerationQueue, metadataExtractionQueue=$metadataExtractionQueue, videoConversionQueue=$videoConversionQueue, objectTaggingQueue=$objectTaggingQueue, clipEncodingQueue=$clipEncodingQueue, storageTemplateMigrationQueue=$storageTemplateMigrationQueue, backgroundTaskQueue=$backgroundTaskQueue, searchQueue=$searchQueue, recognizeFacesQueue=$recognizeFacesQueue, sidecarQueue=$sidecarQueue]';
|
||||
String toString() => 'AllJobStatusResponseDto[thumbnailGeneration=$thumbnailGeneration, metadataExtraction=$metadataExtraction, videoConversion=$videoConversion, objectTagging=$objectTagging, clipEncoding=$clipEncoding, storageTemplateMigration=$storageTemplateMigration, backgroundTask=$backgroundTask, search=$search, recognizeFaces=$recognizeFaces, sidecar=$sidecar]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'thumbnail-generation-queue'] = this.thumbnailGenerationQueue;
|
||||
json[r'metadata-extraction-queue'] = this.metadataExtractionQueue;
|
||||
json[r'video-conversion-queue'] = this.videoConversionQueue;
|
||||
json[r'object-tagging-queue'] = this.objectTaggingQueue;
|
||||
json[r'clip-encoding-queue'] = this.clipEncodingQueue;
|
||||
json[r'storage-template-migration-queue'] = this.storageTemplateMigrationQueue;
|
||||
json[r'background-task-queue'] = this.backgroundTaskQueue;
|
||||
json[r'search-queue'] = this.searchQueue;
|
||||
json[r'recognize-faces-queue'] = this.recognizeFacesQueue;
|
||||
json[r'sidecar-queue'] = this.sidecarQueue;
|
||||
json[r'thumbnailGeneration'] = this.thumbnailGeneration;
|
||||
json[r'metadataExtraction'] = this.metadataExtraction;
|
||||
json[r'videoConversion'] = this.videoConversion;
|
||||
json[r'objectTagging'] = this.objectTagging;
|
||||
json[r'clipEncoding'] = this.clipEncoding;
|
||||
json[r'storageTemplateMigration'] = this.storageTemplateMigration;
|
||||
json[r'backgroundTask'] = this.backgroundTask;
|
||||
json[r'search'] = this.search;
|
||||
json[r'recognizeFaces'] = this.recognizeFaces;
|
||||
json[r'sidecar'] = this.sidecar;
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -109,16 +109,16 @@ class AllJobStatusResponseDto {
|
||||
}());
|
||||
|
||||
return AllJobStatusResponseDto(
|
||||
thumbnailGenerationQueue: JobStatusDto.fromJson(json[r'thumbnail-generation-queue'])!,
|
||||
metadataExtractionQueue: JobStatusDto.fromJson(json[r'metadata-extraction-queue'])!,
|
||||
videoConversionQueue: JobStatusDto.fromJson(json[r'video-conversion-queue'])!,
|
||||
objectTaggingQueue: JobStatusDto.fromJson(json[r'object-tagging-queue'])!,
|
||||
clipEncodingQueue: JobStatusDto.fromJson(json[r'clip-encoding-queue'])!,
|
||||
storageTemplateMigrationQueue: JobStatusDto.fromJson(json[r'storage-template-migration-queue'])!,
|
||||
backgroundTaskQueue: JobStatusDto.fromJson(json[r'background-task-queue'])!,
|
||||
searchQueue: JobStatusDto.fromJson(json[r'search-queue'])!,
|
||||
recognizeFacesQueue: JobStatusDto.fromJson(json[r'recognize-faces-queue'])!,
|
||||
sidecarQueue: JobStatusDto.fromJson(json[r'sidecar-queue'])!,
|
||||
thumbnailGeneration: JobStatusDto.fromJson(json[r'thumbnailGeneration'])!,
|
||||
metadataExtraction: JobStatusDto.fromJson(json[r'metadataExtraction'])!,
|
||||
videoConversion: JobStatusDto.fromJson(json[r'videoConversion'])!,
|
||||
objectTagging: JobStatusDto.fromJson(json[r'objectTagging'])!,
|
||||
clipEncoding: JobStatusDto.fromJson(json[r'clipEncoding'])!,
|
||||
storageTemplateMigration: JobStatusDto.fromJson(json[r'storageTemplateMigration'])!,
|
||||
backgroundTask: JobStatusDto.fromJson(json[r'backgroundTask'])!,
|
||||
search: JobStatusDto.fromJson(json[r'search'])!,
|
||||
recognizeFaces: JobStatusDto.fromJson(json[r'recognizeFaces'])!,
|
||||
sidecar: JobStatusDto.fromJson(json[r'sidecar'])!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
@@ -166,16 +166,16 @@ class AllJobStatusResponseDto {
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'thumbnail-generation-queue',
|
||||
'metadata-extraction-queue',
|
||||
'video-conversion-queue',
|
||||
'object-tagging-queue',
|
||||
'clip-encoding-queue',
|
||||
'storage-template-migration-queue',
|
||||
'background-task-queue',
|
||||
'search-queue',
|
||||
'recognize-faces-queue',
|
||||
'sidecar-queue',
|
||||
'thumbnailGeneration',
|
||||
'metadataExtraction',
|
||||
'videoConversion',
|
||||
'objectTagging',
|
||||
'clipEncoding',
|
||||
'storageTemplateMigration',
|
||||
'backgroundTask',
|
||||
'search',
|
||||
'recognizeFaces',
|
||||
'sidecar',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
111
mobile/openapi/lib/model/asset_ids_dto.dart
generated
Normal file
111
mobile/openapi/lib/model/asset_ids_dto.dart
generated
Normal file
@@ -0,0 +1,111 @@
|
||||
//
|
||||
// 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 AssetIdsDto {
|
||||
/// Returns a new [AssetIdsDto] instance.
|
||||
AssetIdsDto({
|
||||
this.assetIds = const [],
|
||||
});
|
||||
|
||||
List<String> assetIds;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is AssetIdsDto &&
|
||||
other.assetIds == assetIds;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(assetIds.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'AssetIdsDto[assetIds=$assetIds]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'assetIds'] = this.assetIds;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [AssetIdsDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static AssetIdsDto? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
// Ensure that the map contains the required keys.
|
||||
// Note 1: the values aren't checked for validity beyond being non-null.
|
||||
// Note 2: this code is stripped in release mode!
|
||||
assert(() {
|
||||
requiredKeys.forEach((key) {
|
||||
assert(json.containsKey(key), 'Required key "AssetIdsDto[$key]" is missing from JSON.');
|
||||
assert(json[key] != null, 'Required key "AssetIdsDto[$key]" has a null value in JSON.');
|
||||
});
|
||||
return true;
|
||||
}());
|
||||
|
||||
return AssetIdsDto(
|
||||
assetIds: json[r'assetIds'] is Iterable
|
||||
? (json[r'assetIds'] as Iterable).cast<String>().toList(growable: false)
|
||||
: const [],
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<AssetIdsDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <AssetIdsDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = AssetIdsDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, AssetIdsDto> mapFromJson(dynamic json) {
|
||||
final map = <String, AssetIdsDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = AssetIdsDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of AssetIdsDto-objects as value to a dart map
|
||||
static Map<String, List<AssetIdsDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<AssetIdsDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = AssetIdsDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'assetIds',
|
||||
};
|
||||
}
|
||||
|
||||
205
mobile/openapi/lib/model/asset_ids_response_dto.dart
generated
Normal file
205
mobile/openapi/lib/model/asset_ids_response_dto.dart
generated
Normal file
@@ -0,0 +1,205 @@
|
||||
//
|
||||
// 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 AssetIdsResponseDto {
|
||||
/// Returns a new [AssetIdsResponseDto] instance.
|
||||
AssetIdsResponseDto({
|
||||
required this.assetId,
|
||||
required this.success,
|
||||
this.error,
|
||||
});
|
||||
|
||||
String assetId;
|
||||
|
||||
bool success;
|
||||
|
||||
AssetIdsResponseDtoErrorEnum? error;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is AssetIdsResponseDto &&
|
||||
other.assetId == assetId &&
|
||||
other.success == success &&
|
||||
other.error == error;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(assetId.hashCode) +
|
||||
(success.hashCode) +
|
||||
(error == null ? 0 : error!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'AssetIdsResponseDto[assetId=$assetId, success=$success, error=$error]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'assetId'] = this.assetId;
|
||||
json[r'success'] = this.success;
|
||||
if (this.error != null) {
|
||||
json[r'error'] = this.error;
|
||||
} else {
|
||||
// json[r'error'] = null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [AssetIdsResponseDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static AssetIdsResponseDto? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
// Ensure that the map contains the required keys.
|
||||
// Note 1: the values aren't checked for validity beyond being non-null.
|
||||
// Note 2: this code is stripped in release mode!
|
||||
assert(() {
|
||||
requiredKeys.forEach((key) {
|
||||
assert(json.containsKey(key), 'Required key "AssetIdsResponseDto[$key]" is missing from JSON.');
|
||||
assert(json[key] != null, 'Required key "AssetIdsResponseDto[$key]" has a null value in JSON.');
|
||||
});
|
||||
return true;
|
||||
}());
|
||||
|
||||
return AssetIdsResponseDto(
|
||||
assetId: mapValueOfType<String>(json, r'assetId')!,
|
||||
success: mapValueOfType<bool>(json, r'success')!,
|
||||
error: AssetIdsResponseDtoErrorEnum.fromJson(json[r'error']),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<AssetIdsResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <AssetIdsResponseDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = AssetIdsResponseDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, AssetIdsResponseDto> mapFromJson(dynamic json) {
|
||||
final map = <String, AssetIdsResponseDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = AssetIdsResponseDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of AssetIdsResponseDto-objects as value to a dart map
|
||||
static Map<String, List<AssetIdsResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<AssetIdsResponseDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = AssetIdsResponseDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'assetId',
|
||||
'success',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
class AssetIdsResponseDtoErrorEnum {
|
||||
/// Instantiate a new enum with the provided [value].
|
||||
const AssetIdsResponseDtoErrorEnum._(this.value);
|
||||
|
||||
/// The underlying value of this enum member.
|
||||
final String value;
|
||||
|
||||
@override
|
||||
String toString() => value;
|
||||
|
||||
String toJson() => value;
|
||||
|
||||
static const duplicate = AssetIdsResponseDtoErrorEnum._(r'duplicate');
|
||||
static const noPermission = AssetIdsResponseDtoErrorEnum._(r'no_permission');
|
||||
static const notFound = AssetIdsResponseDtoErrorEnum._(r'not_found');
|
||||
|
||||
/// List of all possible values in this [enum][AssetIdsResponseDtoErrorEnum].
|
||||
static const values = <AssetIdsResponseDtoErrorEnum>[
|
||||
duplicate,
|
||||
noPermission,
|
||||
notFound,
|
||||
];
|
||||
|
||||
static AssetIdsResponseDtoErrorEnum? fromJson(dynamic value) => AssetIdsResponseDtoErrorEnumTypeTransformer().decode(value);
|
||||
|
||||
static List<AssetIdsResponseDtoErrorEnum>? listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <AssetIdsResponseDtoErrorEnum>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = AssetIdsResponseDtoErrorEnum.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
}
|
||||
|
||||
/// Transformation class that can [encode] an instance of [AssetIdsResponseDtoErrorEnum] to String,
|
||||
/// and [decode] dynamic data back to [AssetIdsResponseDtoErrorEnum].
|
||||
class AssetIdsResponseDtoErrorEnumTypeTransformer {
|
||||
factory AssetIdsResponseDtoErrorEnumTypeTransformer() => _instance ??= const AssetIdsResponseDtoErrorEnumTypeTransformer._();
|
||||
|
||||
const AssetIdsResponseDtoErrorEnumTypeTransformer._();
|
||||
|
||||
String encode(AssetIdsResponseDtoErrorEnum data) => data.value;
|
||||
|
||||
/// Decodes a [dynamic value][data] to a AssetIdsResponseDtoErrorEnum.
|
||||
///
|
||||
/// 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.
|
||||
AssetIdsResponseDtoErrorEnum? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'duplicate': return AssetIdsResponseDtoErrorEnum.duplicate;
|
||||
case r'no_permission': return AssetIdsResponseDtoErrorEnum.noPermission;
|
||||
case r'not_found': return AssetIdsResponseDtoErrorEnum.notFound;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Singleton [AssetIdsResponseDtoErrorEnumTypeTransformer] instance.
|
||||
static AssetIdsResponseDtoErrorEnumTypeTransformer? _instance;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ class GetAssetCountByTimeBucketDto {
|
||||
GetAssetCountByTimeBucketDto({
|
||||
required this.timeGroup,
|
||||
this.userId,
|
||||
this.withoutThumbs,
|
||||
});
|
||||
|
||||
TimeGroupEnum timeGroup;
|
||||
@@ -27,19 +28,30 @@ class GetAssetCountByTimeBucketDto {
|
||||
///
|
||||
String? userId;
|
||||
|
||||
/// Include assets without thumbnails
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
bool? withoutThumbs;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is GetAssetCountByTimeBucketDto &&
|
||||
other.timeGroup == timeGroup &&
|
||||
other.userId == userId;
|
||||
other.userId == userId &&
|
||||
other.withoutThumbs == withoutThumbs;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(timeGroup.hashCode) +
|
||||
(userId == null ? 0 : userId!.hashCode);
|
||||
(userId == null ? 0 : userId!.hashCode) +
|
||||
(withoutThumbs == null ? 0 : withoutThumbs!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'GetAssetCountByTimeBucketDto[timeGroup=$timeGroup, userId=$userId]';
|
||||
String toString() => 'GetAssetCountByTimeBucketDto[timeGroup=$timeGroup, userId=$userId, withoutThumbs=$withoutThumbs]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -49,6 +61,11 @@ class GetAssetCountByTimeBucketDto {
|
||||
} else {
|
||||
// json[r'userId'] = null;
|
||||
}
|
||||
if (this.withoutThumbs != null) {
|
||||
json[r'withoutThumbs'] = this.withoutThumbs;
|
||||
} else {
|
||||
// json[r'withoutThumbs'] = null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -73,6 +90,7 @@ class GetAssetCountByTimeBucketDto {
|
||||
return GetAssetCountByTimeBucketDto(
|
||||
timeGroup: TimeGroupEnum.fromJson(json[r'timeGroup'])!,
|
||||
userId: mapValueOfType<String>(json, r'userId'),
|
||||
withoutThumbs: mapValueOfType<bool>(json, r'withoutThumbs'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
60
mobile/openapi/lib/model/job_name.dart
generated
60
mobile/openapi/lib/model/job_name.dart
generated
@@ -23,29 +23,29 @@ class JobName {
|
||||
|
||||
String toJson() => value;
|
||||
|
||||
static const thumbnailGenerationQueue = JobName._(r'thumbnail-generation-queue');
|
||||
static const metadataExtractionQueue = JobName._(r'metadata-extraction-queue');
|
||||
static const videoConversionQueue = JobName._(r'video-conversion-queue');
|
||||
static const objectTaggingQueue = JobName._(r'object-tagging-queue');
|
||||
static const recognizeFacesQueue = JobName._(r'recognize-faces-queue');
|
||||
static const clipEncodingQueue = JobName._(r'clip-encoding-queue');
|
||||
static const backgroundTaskQueue = JobName._(r'background-task-queue');
|
||||
static const storageTemplateMigrationQueue = JobName._(r'storage-template-migration-queue');
|
||||
static const searchQueue = JobName._(r'search-queue');
|
||||
static const sidecarQueue = JobName._(r'sidecar-queue');
|
||||
static const thumbnailGeneration = JobName._(r'thumbnailGeneration');
|
||||
static const metadataExtraction = JobName._(r'metadataExtraction');
|
||||
static const videoConversion = JobName._(r'videoConversion');
|
||||
static const objectTagging = JobName._(r'objectTagging');
|
||||
static const recognizeFaces = JobName._(r'recognizeFaces');
|
||||
static const clipEncoding = JobName._(r'clipEncoding');
|
||||
static const backgroundTask = JobName._(r'backgroundTask');
|
||||
static const storageTemplateMigration = JobName._(r'storageTemplateMigration');
|
||||
static const search = JobName._(r'search');
|
||||
static const sidecar = JobName._(r'sidecar');
|
||||
|
||||
/// List of all possible values in this [enum][JobName].
|
||||
static const values = <JobName>[
|
||||
thumbnailGenerationQueue,
|
||||
metadataExtractionQueue,
|
||||
videoConversionQueue,
|
||||
objectTaggingQueue,
|
||||
recognizeFacesQueue,
|
||||
clipEncodingQueue,
|
||||
backgroundTaskQueue,
|
||||
storageTemplateMigrationQueue,
|
||||
searchQueue,
|
||||
sidecarQueue,
|
||||
thumbnailGeneration,
|
||||
metadataExtraction,
|
||||
videoConversion,
|
||||
objectTagging,
|
||||
recognizeFaces,
|
||||
clipEncoding,
|
||||
backgroundTask,
|
||||
storageTemplateMigration,
|
||||
search,
|
||||
sidecar,
|
||||
];
|
||||
|
||||
static JobName? fromJson(dynamic value) => JobNameTypeTransformer().decode(value);
|
||||
@@ -84,16 +84,16 @@ class JobNameTypeTransformer {
|
||||
JobName? decode(dynamic data, {bool allowNull = true}) {
|
||||
if (data != null) {
|
||||
switch (data) {
|
||||
case r'thumbnail-generation-queue': return JobName.thumbnailGenerationQueue;
|
||||
case r'metadata-extraction-queue': return JobName.metadataExtractionQueue;
|
||||
case r'video-conversion-queue': return JobName.videoConversionQueue;
|
||||
case r'object-tagging-queue': return JobName.objectTaggingQueue;
|
||||
case r'recognize-faces-queue': return JobName.recognizeFacesQueue;
|
||||
case r'clip-encoding-queue': return JobName.clipEncodingQueue;
|
||||
case r'background-task-queue': return JobName.backgroundTaskQueue;
|
||||
case r'storage-template-migration-queue': return JobName.storageTemplateMigrationQueue;
|
||||
case r'search-queue': return JobName.searchQueue;
|
||||
case r'sidecar-queue': return JobName.sidecarQueue;
|
||||
case r'thumbnailGeneration': return JobName.thumbnailGeneration;
|
||||
case r'metadataExtraction': return JobName.metadataExtraction;
|
||||
case r'videoConversion': return JobName.videoConversion;
|
||||
case r'objectTagging': return JobName.objectTagging;
|
||||
case r'recognizeFaces': return JobName.recognizeFaces;
|
||||
case r'clipEncoding': return JobName.clipEncoding;
|
||||
case r'backgroundTask': return JobName.backgroundTask;
|
||||
case r'storageTemplateMigration': return JobName.storageTemplateMigration;
|
||||
case r'search': return JobName.search;
|
||||
case r'sidecar': return JobName.sidecar;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
|
||||
109
mobile/openapi/lib/model/job_settings_dto.dart
generated
Normal file
109
mobile/openapi/lib/model/job_settings_dto.dart
generated
Normal file
@@ -0,0 +1,109 @@
|
||||
//
|
||||
// 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 JobSettingsDto {
|
||||
/// Returns a new [JobSettingsDto] instance.
|
||||
JobSettingsDto({
|
||||
required this.concurrency,
|
||||
});
|
||||
|
||||
int concurrency;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is JobSettingsDto &&
|
||||
other.concurrency == concurrency;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(concurrency.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'JobSettingsDto[concurrency=$concurrency]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'concurrency'] = this.concurrency;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [JobSettingsDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static JobSettingsDto? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
// Ensure that the map contains the required keys.
|
||||
// Note 1: the values aren't checked for validity beyond being non-null.
|
||||
// Note 2: this code is stripped in release mode!
|
||||
assert(() {
|
||||
requiredKeys.forEach((key) {
|
||||
assert(json.containsKey(key), 'Required key "JobSettingsDto[$key]" is missing from JSON.');
|
||||
assert(json[key] != null, 'Required key "JobSettingsDto[$key]" has a null value in JSON.');
|
||||
});
|
||||
return true;
|
||||
}());
|
||||
|
||||
return JobSettingsDto(
|
||||
concurrency: mapValueOfType<int>(json, r'concurrency')!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<JobSettingsDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <JobSettingsDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = JobSettingsDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, JobSettingsDto> mapFromJson(dynamic json) {
|
||||
final map = <String, JobSettingsDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = JobSettingsDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of JobSettingsDto-objects as value to a dart map
|
||||
static Map<String, List<JobSettingsDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<JobSettingsDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = JobSettingsDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'concurrency',
|
||||
};
|
||||
}
|
||||
|
||||
14
mobile/openapi/lib/model/system_config_dto.dart
generated
14
mobile/openapi/lib/model/system_config_dto.dart
generated
@@ -17,6 +17,7 @@ class SystemConfigDto {
|
||||
required this.oauth,
|
||||
required this.passwordLogin,
|
||||
required this.storageTemplate,
|
||||
required this.job,
|
||||
});
|
||||
|
||||
SystemConfigFFmpegDto ffmpeg;
|
||||
@@ -27,12 +28,15 @@ class SystemConfigDto {
|
||||
|
||||
SystemConfigStorageTemplateDto storageTemplate;
|
||||
|
||||
SystemConfigJobDto job;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SystemConfigDto &&
|
||||
other.ffmpeg == ffmpeg &&
|
||||
other.oauth == oauth &&
|
||||
other.passwordLogin == passwordLogin &&
|
||||
other.storageTemplate == storageTemplate;
|
||||
other.storageTemplate == storageTemplate &&
|
||||
other.job == job;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
@@ -40,10 +44,11 @@ class SystemConfigDto {
|
||||
(ffmpeg.hashCode) +
|
||||
(oauth.hashCode) +
|
||||
(passwordLogin.hashCode) +
|
||||
(storageTemplate.hashCode);
|
||||
(storageTemplate.hashCode) +
|
||||
(job.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, oauth=$oauth, passwordLogin=$passwordLogin, storageTemplate=$storageTemplate]';
|
||||
String toString() => 'SystemConfigDto[ffmpeg=$ffmpeg, oauth=$oauth, passwordLogin=$passwordLogin, storageTemplate=$storageTemplate, job=$job]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -51,6 +56,7 @@ class SystemConfigDto {
|
||||
json[r'oauth'] = this.oauth;
|
||||
json[r'passwordLogin'] = this.passwordLogin;
|
||||
json[r'storageTemplate'] = this.storageTemplate;
|
||||
json[r'job'] = this.job;
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -77,6 +83,7 @@ class SystemConfigDto {
|
||||
oauth: SystemConfigOAuthDto.fromJson(json[r'oauth'])!,
|
||||
passwordLogin: SystemConfigPasswordLoginDto.fromJson(json[r'passwordLogin'])!,
|
||||
storageTemplate: SystemConfigStorageTemplateDto.fromJson(json[r'storageTemplate'])!,
|
||||
job: SystemConfigJobDto.fromJson(json[r'job'])!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
@@ -128,6 +135,7 @@ class SystemConfigDto {
|
||||
'oauth',
|
||||
'passwordLogin',
|
||||
'storageTemplate',
|
||||
'job',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
181
mobile/openapi/lib/model/system_config_job_dto.dart
generated
Normal file
181
mobile/openapi/lib/model/system_config_job_dto.dart
generated
Normal file
@@ -0,0 +1,181 @@
|
||||
//
|
||||
// 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 SystemConfigJobDto {
|
||||
/// Returns a new [SystemConfigJobDto] instance.
|
||||
SystemConfigJobDto({
|
||||
required this.thumbnailGeneration,
|
||||
required this.metadataExtraction,
|
||||
required this.videoConversion,
|
||||
required this.objectTagging,
|
||||
required this.clipEncoding,
|
||||
required this.storageTemplateMigration,
|
||||
required this.backgroundTask,
|
||||
required this.search,
|
||||
required this.recognizeFaces,
|
||||
required this.sidecar,
|
||||
});
|
||||
|
||||
JobSettingsDto thumbnailGeneration;
|
||||
|
||||
JobSettingsDto metadataExtraction;
|
||||
|
||||
JobSettingsDto videoConversion;
|
||||
|
||||
JobSettingsDto objectTagging;
|
||||
|
||||
JobSettingsDto clipEncoding;
|
||||
|
||||
JobSettingsDto storageTemplateMigration;
|
||||
|
||||
JobSettingsDto backgroundTask;
|
||||
|
||||
JobSettingsDto search;
|
||||
|
||||
JobSettingsDto recognizeFaces;
|
||||
|
||||
JobSettingsDto sidecar;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SystemConfigJobDto &&
|
||||
other.thumbnailGeneration == thumbnailGeneration &&
|
||||
other.metadataExtraction == metadataExtraction &&
|
||||
other.videoConversion == videoConversion &&
|
||||
other.objectTagging == objectTagging &&
|
||||
other.clipEncoding == clipEncoding &&
|
||||
other.storageTemplateMigration == storageTemplateMigration &&
|
||||
other.backgroundTask == backgroundTask &&
|
||||
other.search == search &&
|
||||
other.recognizeFaces == recognizeFaces &&
|
||||
other.sidecar == sidecar;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(thumbnailGeneration.hashCode) +
|
||||
(metadataExtraction.hashCode) +
|
||||
(videoConversion.hashCode) +
|
||||
(objectTagging.hashCode) +
|
||||
(clipEncoding.hashCode) +
|
||||
(storageTemplateMigration.hashCode) +
|
||||
(backgroundTask.hashCode) +
|
||||
(search.hashCode) +
|
||||
(recognizeFaces.hashCode) +
|
||||
(sidecar.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SystemConfigJobDto[thumbnailGeneration=$thumbnailGeneration, metadataExtraction=$metadataExtraction, videoConversion=$videoConversion, objectTagging=$objectTagging, clipEncoding=$clipEncoding, storageTemplateMigration=$storageTemplateMigration, backgroundTask=$backgroundTask, search=$search, recognizeFaces=$recognizeFaces, sidecar=$sidecar]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'thumbnailGeneration'] = this.thumbnailGeneration;
|
||||
json[r'metadataExtraction'] = this.metadataExtraction;
|
||||
json[r'videoConversion'] = this.videoConversion;
|
||||
json[r'objectTagging'] = this.objectTagging;
|
||||
json[r'clipEncoding'] = this.clipEncoding;
|
||||
json[r'storageTemplateMigration'] = this.storageTemplateMigration;
|
||||
json[r'backgroundTask'] = this.backgroundTask;
|
||||
json[r'search'] = this.search;
|
||||
json[r'recognizeFaces'] = this.recognizeFaces;
|
||||
json[r'sidecar'] = this.sidecar;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [SystemConfigJobDto] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static SystemConfigJobDto? fromJson(dynamic value) {
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
// Ensure that the map contains the required keys.
|
||||
// Note 1: the values aren't checked for validity beyond being non-null.
|
||||
// Note 2: this code is stripped in release mode!
|
||||
assert(() {
|
||||
requiredKeys.forEach((key) {
|
||||
assert(json.containsKey(key), 'Required key "SystemConfigJobDto[$key]" is missing from JSON.');
|
||||
assert(json[key] != null, 'Required key "SystemConfigJobDto[$key]" has a null value in JSON.');
|
||||
});
|
||||
return true;
|
||||
}());
|
||||
|
||||
return SystemConfigJobDto(
|
||||
thumbnailGeneration: JobSettingsDto.fromJson(json[r'thumbnailGeneration'])!,
|
||||
metadataExtraction: JobSettingsDto.fromJson(json[r'metadataExtraction'])!,
|
||||
videoConversion: JobSettingsDto.fromJson(json[r'videoConversion'])!,
|
||||
objectTagging: JobSettingsDto.fromJson(json[r'objectTagging'])!,
|
||||
clipEncoding: JobSettingsDto.fromJson(json[r'clipEncoding'])!,
|
||||
storageTemplateMigration: JobSettingsDto.fromJson(json[r'storageTemplateMigration'])!,
|
||||
backgroundTask: JobSettingsDto.fromJson(json[r'backgroundTask'])!,
|
||||
search: JobSettingsDto.fromJson(json[r'search'])!,
|
||||
recognizeFaces: JobSettingsDto.fromJson(json[r'recognizeFaces'])!,
|
||||
sidecar: JobSettingsDto.fromJson(json[r'sidecar'])!,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<SystemConfigJobDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SystemConfigJobDto>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SystemConfigJobDto.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, SystemConfigJobDto> mapFromJson(dynamic json) {
|
||||
final map = <String, SystemConfigJobDto>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = SystemConfigJobDto.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of SystemConfigJobDto-objects as value to a dart map
|
||||
static Map<String, List<SystemConfigJobDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<SystemConfigJobDto>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = SystemConfigJobDto.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'thumbnailGeneration',
|
||||
'metadataExtraction',
|
||||
'videoConversion',
|
||||
'objectTagging',
|
||||
'clipEncoding',
|
||||
'storageTemplateMigration',
|
||||
'backgroundTask',
|
||||
'search',
|
||||
'recognizeFaces',
|
||||
'sidecar',
|
||||
};
|
||||
}
|
||||
|
||||
33
mobile/openapi/lib/model/tag_response_dto.dart
generated
33
mobile/openapi/lib/model/tag_response_dto.dart
generated
@@ -13,54 +13,44 @@ part of openapi.api;
|
||||
class TagResponseDto {
|
||||
/// Returns a new [TagResponseDto] instance.
|
||||
TagResponseDto({
|
||||
required this.id,
|
||||
required this.type,
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.userId,
|
||||
this.renameTagId,
|
||||
});
|
||||
|
||||
String id;
|
||||
|
||||
TagTypeEnum type;
|
||||
|
||||
String id;
|
||||
|
||||
String name;
|
||||
|
||||
String userId;
|
||||
|
||||
String? renameTagId;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is TagResponseDto &&
|
||||
other.id == id &&
|
||||
other.type == type &&
|
||||
other.id == id &&
|
||||
other.name == name &&
|
||||
other.userId == userId &&
|
||||
other.renameTagId == renameTagId;
|
||||
other.userId == userId;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(id.hashCode) +
|
||||
(type.hashCode) +
|
||||
(id.hashCode) +
|
||||
(name.hashCode) +
|
||||
(userId.hashCode) +
|
||||
(renameTagId == null ? 0 : renameTagId!.hashCode);
|
||||
(userId.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'TagResponseDto[id=$id, type=$type, name=$name, userId=$userId, renameTagId=$renameTagId]';
|
||||
String toString() => 'TagResponseDto[type=$type, id=$id, name=$name, userId=$userId]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'id'] = this.id;
|
||||
json[r'type'] = this.type;
|
||||
json[r'id'] = this.id;
|
||||
json[r'name'] = this.name;
|
||||
json[r'userId'] = this.userId;
|
||||
if (this.renameTagId != null) {
|
||||
json[r'renameTagId'] = this.renameTagId;
|
||||
} else {
|
||||
// json[r'renameTagId'] = null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -83,11 +73,10 @@ class TagResponseDto {
|
||||
}());
|
||||
|
||||
return TagResponseDto(
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
type: TagTypeEnum.fromJson(json[r'type'])!,
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
name: mapValueOfType<String>(json, r'name')!,
|
||||
userId: mapValueOfType<String>(json, r'userId')!,
|
||||
renameTagId: mapValueOfType<String>(json, r'renameTagId'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
@@ -135,8 +124,8 @@ class TagResponseDto {
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'id',
|
||||
'type',
|
||||
'id',
|
||||
'name',
|
||||
'userId',
|
||||
};
|
||||
|
||||
23
mobile/openapi/lib/model/update_tag_dto.dart
generated
23
mobile/openapi/lib/model/update_tag_dto.dart
generated
@@ -14,7 +14,6 @@ class UpdateTagDto {
|
||||
/// Returns a new [UpdateTagDto] instance.
|
||||
UpdateTagDto({
|
||||
this.name,
|
||||
this.renameTagId,
|
||||
});
|
||||
|
||||
///
|
||||
@@ -25,27 +24,17 @@ class UpdateTagDto {
|
||||
///
|
||||
String? name;
|
||||
|
||||
///
|
||||
/// 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? renameTagId;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is UpdateTagDto &&
|
||||
other.name == name &&
|
||||
other.renameTagId == renameTagId;
|
||||
other.name == name;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(name == null ? 0 : name!.hashCode) +
|
||||
(renameTagId == null ? 0 : renameTagId!.hashCode);
|
||||
(name == null ? 0 : name!.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'UpdateTagDto[name=$name, renameTagId=$renameTagId]';
|
||||
String toString() => 'UpdateTagDto[name=$name]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
@@ -54,11 +43,6 @@ class UpdateTagDto {
|
||||
} else {
|
||||
// json[r'name'] = null;
|
||||
}
|
||||
if (this.renameTagId != null) {
|
||||
json[r'renameTagId'] = this.renameTagId;
|
||||
} else {
|
||||
// json[r'renameTagId'] = null;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
@@ -82,7 +66,6 @@ class UpdateTagDto {
|
||||
|
||||
return UpdateTagDto(
|
||||
name: mapValueOfType<String>(json, r'name'),
|
||||
renameTagId: mapValueOfType<String>(json, r'renameTagId'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -16,53 +16,53 @@ void main() {
|
||||
// final instance = AllJobStatusResponseDto();
|
||||
|
||||
group('test AllJobStatusResponseDto', () {
|
||||
// JobStatusDto thumbnailGenerationQueue
|
||||
test('to test the property `thumbnailGenerationQueue`', () async {
|
||||
// JobStatusDto thumbnailGeneration
|
||||
test('to test the property `thumbnailGeneration`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobStatusDto metadataExtractionQueue
|
||||
test('to test the property `metadataExtractionQueue`', () async {
|
||||
// JobStatusDto metadataExtraction
|
||||
test('to test the property `metadataExtraction`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobStatusDto videoConversionQueue
|
||||
test('to test the property `videoConversionQueue`', () async {
|
||||
// JobStatusDto videoConversion
|
||||
test('to test the property `videoConversion`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobStatusDto objectTaggingQueue
|
||||
test('to test the property `objectTaggingQueue`', () async {
|
||||
// JobStatusDto objectTagging
|
||||
test('to test the property `objectTagging`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobStatusDto clipEncodingQueue
|
||||
test('to test the property `clipEncodingQueue`', () async {
|
||||
// JobStatusDto clipEncoding
|
||||
test('to test the property `clipEncoding`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobStatusDto storageTemplateMigrationQueue
|
||||
test('to test the property `storageTemplateMigrationQueue`', () async {
|
||||
// JobStatusDto storageTemplateMigration
|
||||
test('to test the property `storageTemplateMigration`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobStatusDto backgroundTaskQueue
|
||||
test('to test the property `backgroundTaskQueue`', () async {
|
||||
// JobStatusDto backgroundTask
|
||||
test('to test the property `backgroundTask`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobStatusDto searchQueue
|
||||
test('to test the property `searchQueue`', () async {
|
||||
// JobStatusDto search
|
||||
test('to test the property `search`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobStatusDto recognizeFacesQueue
|
||||
test('to test the property `recognizeFacesQueue`', () async {
|
||||
// JobStatusDto recognizeFaces
|
||||
test('to test the property `recognizeFaces`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobStatusDto sidecarQueue
|
||||
test('to test the property `sidecarQueue`', () async {
|
||||
// JobStatusDto sidecar
|
||||
test('to test the property `sidecar`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
12
mobile/openapi/test/asset_api_test.dart
generated
12
mobile/openapi/test/asset_api_test.dart
generated
@@ -53,7 +53,7 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<MultipartFile> downloadFile(String assetId, { String key }) async
|
||||
//Future<MultipartFile> downloadFile(String id, { String key }) async
|
||||
test('test downloadFile', () async {
|
||||
// TODO
|
||||
});
|
||||
@@ -72,7 +72,7 @@ void main() {
|
||||
|
||||
// Get all AssetEntity belong to the user
|
||||
//
|
||||
//Future<List<AssetResponseDto>> getAllAssets({ String userId, bool isFavorite, bool isArchived, num skip, String ifNoneMatch }) async
|
||||
//Future<List<AssetResponseDto>> getAllAssets({ String userId, bool isFavorite, bool isArchived, bool withoutThumbs, num skip, String ifNoneMatch }) async
|
||||
test('test getAllAssets', () async {
|
||||
// TODO
|
||||
});
|
||||
@@ -84,7 +84,7 @@ void main() {
|
||||
|
||||
// Get a single asset's information
|
||||
//
|
||||
//Future<AssetResponseDto> getAssetById(String assetId, { String key }) async
|
||||
//Future<AssetResponseDto> getAssetById(String id, { String key }) async
|
||||
test('test getAssetById', () async {
|
||||
// TODO
|
||||
});
|
||||
@@ -109,7 +109,7 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<MultipartFile> getAssetThumbnail(String assetId, { ThumbnailFormat format, String key }) async
|
||||
//Future<MultipartFile> getAssetThumbnail(String id, { ThumbnailFormat format, String key }) async
|
||||
test('test getAssetThumbnail', () async {
|
||||
// TODO
|
||||
});
|
||||
@@ -146,14 +146,14 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<MultipartFile> serveFile(String assetId, { bool isThumb, bool isWeb, String key }) async
|
||||
//Future<MultipartFile> serveFile(String id, { bool isThumb, bool isWeb, String key }) async
|
||||
test('test serveFile', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// Update an asset
|
||||
//
|
||||
//Future<AssetResponseDto> updateAsset(String assetId, UpdateAssetDto updateAssetDto) async
|
||||
//Future<AssetResponseDto> updateAsset(String id, UpdateAssetDto updateAssetDto) async
|
||||
test('test updateAsset', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
27
mobile/openapi/test/asset_ids_dto_test.dart
generated
Normal file
27
mobile/openapi/test/asset_ids_dto_test.dart
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// 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 AssetIdsDto
|
||||
void main() {
|
||||
// final instance = AssetIdsDto();
|
||||
|
||||
group('test AssetIdsDto', () {
|
||||
// List<String> assetIds (default value: const [])
|
||||
test('to test the property `assetIds`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
37
mobile/openapi/test/asset_ids_response_dto_test.dart
generated
Normal file
37
mobile/openapi/test/asset_ids_response_dto_test.dart
generated
Normal file
@@ -0,0 +1,37 @@
|
||||
//
|
||||
// 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 AssetIdsResponseDto
|
||||
void main() {
|
||||
// final instance = AssetIdsResponseDto();
|
||||
|
||||
group('test AssetIdsResponseDto', () {
|
||||
// String assetId
|
||||
test('to test the property `assetId`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// bool success
|
||||
test('to test the property `success`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String error
|
||||
test('to test the property `error`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
@@ -26,6 +26,12 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// Include assets without thumbnails
|
||||
// bool withoutThumbs
|
||||
test('to test the property `withoutThumbs`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
27
mobile/openapi/test/job_settings_dto_test.dart
generated
Normal file
27
mobile/openapi/test/job_settings_dto_test.dart
generated
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// 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 JobSettingsDto
|
||||
void main() {
|
||||
// final instance = JobSettingsDto();
|
||||
|
||||
group('test JobSettingsDto', () {
|
||||
// int concurrency
|
||||
test('to test the property `concurrency`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
10
mobile/openapi/test/share_api_test.dart
generated
10
mobile/openapi/test/share_api_test.dart
generated
@@ -17,11 +17,6 @@ void main() {
|
||||
// final instance = ShareApi();
|
||||
|
||||
group('tests for ShareApi', () {
|
||||
//Future<SharedLinkResponseDto> editSharedLink(String id, EditSharedLinkDto editSharedLinkDto) async
|
||||
test('test editSharedLink', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<List<SharedLinkResponseDto>> getAllSharedLinks() async
|
||||
test('test getAllSharedLinks', () async {
|
||||
// TODO
|
||||
@@ -42,5 +37,10 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<SharedLinkResponseDto> updateSharedLink(String id, EditSharedLinkDto editSharedLinkDto) async
|
||||
test('test updateSharedLink', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
5
mobile/openapi/test/system_config_dto_test.dart
generated
5
mobile/openapi/test/system_config_dto_test.dart
generated
@@ -36,6 +36,11 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// SystemConfigJobDto job
|
||||
test('to test the property `job`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
72
mobile/openapi/test/system_config_job_dto_test.dart
generated
Normal file
72
mobile/openapi/test/system_config_job_dto_test.dart
generated
Normal file
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// 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 SystemConfigJobDto
|
||||
void main() {
|
||||
// final instance = SystemConfigJobDto();
|
||||
|
||||
group('test SystemConfigJobDto', () {
|
||||
// JobSettingsDto thumbnailGeneration
|
||||
test('to test the property `thumbnailGeneration`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobSettingsDto metadataExtraction
|
||||
test('to test the property `metadataExtraction`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobSettingsDto videoConversion
|
||||
test('to test the property `videoConversion`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobSettingsDto objectTagging
|
||||
test('to test the property `objectTagging`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobSettingsDto clipEncoding
|
||||
test('to test the property `clipEncoding`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobSettingsDto storageTemplateMigration
|
||||
test('to test the property `storageTemplateMigration`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobSettingsDto backgroundTask
|
||||
test('to test the property `backgroundTask`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobSettingsDto search
|
||||
test('to test the property `search`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobSettingsDto recognizeFaces
|
||||
test('to test the property `recognizeFaces`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// JobSettingsDto sidecar
|
||||
test('to test the property `sidecar`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
35
mobile/openapi/test/tag_api_test.dart
generated
35
mobile/openapi/test/tag_api_test.dart
generated
@@ -17,28 +17,43 @@ void main() {
|
||||
// final instance = TagApi();
|
||||
|
||||
group('tests for TagApi', () {
|
||||
//Future<TagResponseDto> create(CreateTagDto createTagDto) async
|
||||
test('test create', () async {
|
||||
//Future<TagResponseDto> createTag(CreateTagDto createTagDto) async
|
||||
test('test createTag', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future delete(String id) async
|
||||
test('test delete', () async {
|
||||
//Future deleteTag(String id) async
|
||||
test('test deleteTag', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<List<TagResponseDto>> findAll() async
|
||||
test('test findAll', () async {
|
||||
//Future<List<TagResponseDto>> getAllTags() async
|
||||
test('test getAllTags', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<TagResponseDto> findOne(String id) async
|
||||
test('test findOne', () async {
|
||||
//Future<List<AssetResponseDto>> getTagAssets(String id) async
|
||||
test('test getTagAssets', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<TagResponseDto> update(String id, UpdateTagDto updateTagDto) async
|
||||
test('test update', () async {
|
||||
//Future<TagResponseDto> getTagById(String id) async
|
||||
test('test getTagById', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<List<AssetIdsResponseDto>> tagAssets(String id, AssetIdsDto assetIdsDto) async
|
||||
test('test tagAssets', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<List<AssetIdsResponseDto>> untagAssets(String id, AssetIdsDto assetIdsDto) async
|
||||
test('test untagAssets', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
//Future<TagResponseDto> updateTag(String id, UpdateTagDto updateTagDto) async
|
||||
test('test updateTag', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
13
mobile/openapi/test/tag_response_dto_test.dart
generated
13
mobile/openapi/test/tag_response_dto_test.dart
generated
@@ -16,13 +16,13 @@ void main() {
|
||||
// final instance = TagResponseDto();
|
||||
|
||||
group('test TagResponseDto', () {
|
||||
// String id
|
||||
test('to test the property `id`', () async {
|
||||
// TagTypeEnum type
|
||||
test('to test the property `type`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// TagTypeEnum type
|
||||
test('to test the property `type`', () async {
|
||||
// String id
|
||||
test('to test the property `id`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
@@ -36,11 +36,6 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String renameTagId
|
||||
test('to test the property `renameTagId`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
5
mobile/openapi/test/update_tag_dto_test.dart
generated
5
mobile/openapi/test/update_tag_dto_test.dart
generated
@@ -21,11 +21,6 @@ void main() {
|
||||
// TODO
|
||||
});
|
||||
|
||||
// String renameTagId
|
||||
test('to test the property `renameTagId`', () async {
|
||||
// TODO
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ name: immich_mobile
|
||||
description: Immich - selfhosted backup media file on mobile phone
|
||||
|
||||
publish_to: "none"
|
||||
version: 1.59.0+82
|
||||
version: 1.60.0+83
|
||||
isar_version: &isar_version 3.1.0+1
|
||||
|
||||
environment:
|
||||
|
||||
@@ -11,24 +11,11 @@ import { AlbumResponseDto } from '@app/domain';
|
||||
import { AlbumCountResponseDto } from './response-dto/album-count-response.dto';
|
||||
import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto';
|
||||
import { Response as Res } from 'express';
|
||||
import {
|
||||
IMMICH_ARCHIVE_COMPLETE,
|
||||
IMMICH_ARCHIVE_FILE_COUNT,
|
||||
IMMICH_CONTENT_LENGTH_HINT,
|
||||
} from '../../constants/download.constant';
|
||||
import { DownloadDto } from '../asset/dto/download-library.dto';
|
||||
import { CreateAlbumShareLinkDto as CreateAlbumSharedLinkDto } from './dto/create-album-shared-link.dto';
|
||||
import { UseValidation } from '../../decorators/use-validation.decorator';
|
||||
import { UUIDParamDto } from '../../controllers/dto/uuid-param.dto';
|
||||
import { DownloadArchive } from '../../modules/download/download.service';
|
||||
|
||||
const handleDownload = (download: DownloadArchive, res: Res) => {
|
||||
res.attachment(download.fileName);
|
||||
res.setHeader(IMMICH_CONTENT_LENGTH_HINT, download.fileSize);
|
||||
res.setHeader(IMMICH_ARCHIVE_FILE_COUNT, download.fileCount);
|
||||
res.setHeader(IMMICH_ARCHIVE_COMPLETE, `${download.complete}`);
|
||||
return download.stream;
|
||||
};
|
||||
import { handleDownload } from '../../app.utils';
|
||||
|
||||
@ApiTags('Album')
|
||||
@Controller('album')
|
||||
|
||||
@@ -10,13 +10,19 @@ import { AddAssetsResponseDto } from './response-dto/add-assets-response.dto';
|
||||
import { AddAssetsDto } from './dto/add-assets.dto';
|
||||
import { DownloadService } from '../../modules/download/download.service';
|
||||
import { DownloadDto } from '../asset/dto/download-library.dto';
|
||||
import { ShareCore, ISharedLinkRepository, mapSharedLink, SharedLinkResponseDto, ICryptoRepository } from '@app/domain';
|
||||
import {
|
||||
SharedLinkCore,
|
||||
ISharedLinkRepository,
|
||||
mapSharedLink,
|
||||
SharedLinkResponseDto,
|
||||
ICryptoRepository,
|
||||
} from '@app/domain';
|
||||
import { CreateAlbumShareLinkDto } from './dto/create-album-shared-link.dto';
|
||||
|
||||
@Injectable()
|
||||
export class AlbumService {
|
||||
readonly logger = new Logger(AlbumService.name);
|
||||
private shareCore: ShareCore;
|
||||
private shareCore: SharedLinkCore;
|
||||
|
||||
constructor(
|
||||
@Inject(IAlbumRepository) private albumRepository: IAlbumRepository,
|
||||
@@ -25,7 +31,7 @@ export class AlbumService {
|
||||
@Inject(ICryptoRepository) cryptoRepository: ICryptoRepository,
|
||||
@Inject(IJobRepository) private jobRepository: IJobRepository,
|
||||
) {
|
||||
this.shareCore = new ShareCore(sharedLinkRepository, cryptoRepository);
|
||||
this.shareCore = new SharedLinkCore(sharedLinkRepository, cryptoRepository);
|
||||
}
|
||||
|
||||
private async _getAlbum({
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import { SearchPropertiesDto } from './dto/search-properties.dto';
|
||||
import { CuratedLocationsResponseDto } from './response-dto/curated-locations-response.dto';
|
||||
import { AssetEntity, AssetType, ExifEntity } from '@app/infra/entities';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm/repository/Repository';
|
||||
import { CuratedObjectsResponseDto } from './response-dto/curated-objects-response.dto';
|
||||
import { AssetCountByTimeBucket } from './response-dto/asset-count-by-time-group-response.dto';
|
||||
import { TimeGroupEnum } from './dto/get-asset-count-by-time-bucket.dto';
|
||||
import { GetAssetCountByTimeBucketDto, TimeGroupEnum } from './dto/get-asset-count-by-time-bucket.dto';
|
||||
import { GetAssetByTimeBucketDto } from './dto/get-asset-by-time-bucket.dto';
|
||||
import { AssetCountByUserIdResponseDto } from './response-dto/asset-count-by-user-id-response.dto';
|
||||
import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto';
|
||||
import { In } from 'typeorm/find-options/operator/In';
|
||||
import { UpdateAssetDto } from './dto/update-asset.dto';
|
||||
import { ITagRepository } from '../tag/tag.repository';
|
||||
import { IsNull, Not } from 'typeorm';
|
||||
import { AssetSearchDto } from './dto/asset-search.dto';
|
||||
|
||||
@@ -38,7 +37,7 @@ export interface IAssetRepository {
|
||||
getLocationsByUserId(userId: string): Promise<CuratedLocationsResponseDto[]>;
|
||||
getDetectedObjectsByUserId(userId: string): Promise<CuratedObjectsResponseDto[]>;
|
||||
getSearchPropertiesByUserId(userId: string): Promise<SearchPropertiesDto[]>;
|
||||
getAssetCountByTimeBucket(userId: string, timeBucket: TimeGroupEnum): Promise<AssetCountByTimeBucket[]>;
|
||||
getAssetCountByTimeBucket(userId: string, dto: GetAssetCountByTimeBucketDto): Promise<AssetCountByTimeBucket[]>;
|
||||
getAssetCountByUserId(userId: string): Promise<AssetCountByUserIdResponseDto>;
|
||||
getArchivedAssetCountByUserId(userId: string): Promise<AssetCountByUserIdResponseDto>;
|
||||
getAssetByTimeBucket(userId: string, getAssetByTimeBucketDto: GetAssetByTimeBucketDto): Promise<AssetEntity[]>;
|
||||
@@ -52,10 +51,7 @@ export const IAssetRepository = 'IAssetRepository';
|
||||
@Injectable()
|
||||
export class AssetRepository implements IAssetRepository {
|
||||
constructor(
|
||||
@InjectRepository(AssetEntity)
|
||||
private assetRepository: Repository<AssetEntity>,
|
||||
|
||||
@Inject(ITagRepository) private _tagRepository: ITagRepository,
|
||||
@InjectRepository(AssetEntity) private assetRepository: Repository<AssetEntity>,
|
||||
@InjectRepository(ExifEntity) private exifRepository: Repository<ExifEntity>,
|
||||
) {}
|
||||
|
||||
@@ -123,36 +119,35 @@ export class AssetRepository implements IAssetRepository {
|
||||
return builder.getMany();
|
||||
}
|
||||
|
||||
async getAssetCountByTimeBucket(userId: string, timeBucket: TimeGroupEnum) {
|
||||
let result: AssetCountByTimeBucket[] = [];
|
||||
async getAssetCountByTimeBucket(
|
||||
userId: string,
|
||||
dto: GetAssetCountByTimeBucketDto,
|
||||
): Promise<AssetCountByTimeBucket[]> {
|
||||
const builder = this.assetRepository
|
||||
.createQueryBuilder('asset')
|
||||
.select(`COUNT(asset.id)::int`, 'count')
|
||||
.where('"ownerId" = :userId', { userId: userId })
|
||||
.andWhere('asset.isVisible = true')
|
||||
.andWhere('asset.isArchived = false');
|
||||
|
||||
if (timeBucket === TimeGroupEnum.Month) {
|
||||
result = await this.assetRepository
|
||||
.createQueryBuilder('asset')
|
||||
.select(`COUNT(asset.id)::int`, 'count')
|
||||
// Using a parameter for this doesn't work https://github.com/typeorm/typeorm/issues/7308
|
||||
if (dto.timeGroup === TimeGroupEnum.Month) {
|
||||
builder
|
||||
.addSelect(`date_trunc('month', "fileCreatedAt")`, 'timeBucket')
|
||||
.where('"ownerId" = :userId', { userId: userId })
|
||||
.andWhere('asset.resizePath is not NULL')
|
||||
.andWhere('asset.isVisible = true')
|
||||
.andWhere('asset.isArchived = false')
|
||||
.groupBy(`date_trunc('month', "fileCreatedAt")`)
|
||||
.orderBy(`date_trunc('month', "fileCreatedAt")`, 'DESC')
|
||||
.getRawMany();
|
||||
} else if (timeBucket === TimeGroupEnum.Day) {
|
||||
result = await this.assetRepository
|
||||
.createQueryBuilder('asset')
|
||||
.select(`COUNT(asset.id)::int`, 'count')
|
||||
.orderBy(`date_trunc('month', "fileCreatedAt")`, 'DESC');
|
||||
} else if (dto.timeGroup === TimeGroupEnum.Day) {
|
||||
builder
|
||||
.addSelect(`date_trunc('day', "fileCreatedAt")`, 'timeBucket')
|
||||
.where('"ownerId" = :userId', { userId: userId })
|
||||
.andWhere('asset.resizePath is not NULL')
|
||||
.andWhere('asset.isVisible = true')
|
||||
.andWhere('asset.isArchived = false')
|
||||
.groupBy(`date_trunc('day', "fileCreatedAt")`)
|
||||
.orderBy(`date_trunc('day', "fileCreatedAt")`, 'DESC')
|
||||
.getRawMany();
|
||||
.orderBy(`date_trunc('day', "fileCreatedAt")`, 'DESC');
|
||||
}
|
||||
|
||||
return result;
|
||||
if (!dto.withoutThumbs) {
|
||||
builder.andWhere('asset.resizePath is not NULL');
|
||||
}
|
||||
|
||||
return builder.getRawMany();
|
||||
}
|
||||
|
||||
async getSearchPropertiesByUserId(userId: string): Promise<SearchPropertiesDto[]> {
|
||||
@@ -235,7 +230,7 @@ export class AssetRepository implements IAssetRepository {
|
||||
return this.assetRepository.find({
|
||||
where: {
|
||||
ownerId,
|
||||
resizePath: Not(IsNull()),
|
||||
resizePath: dto.withoutThumbs ? undefined : Not(IsNull()),
|
||||
isVisible: true,
|
||||
isFavorite: dto.isFavorite,
|
||||
isArchived: dto.isArchived,
|
||||
|
||||
@@ -45,11 +45,6 @@ import { CheckExistingAssetsDto } from './dto/check-existing-assets.dto';
|
||||
import { CheckExistingAssetsResponseDto } from './response-dto/check-existing-assets-response.dto';
|
||||
import { UpdateAssetDto } from './dto/update-asset.dto';
|
||||
import { DownloadDto } from './dto/download-library.dto';
|
||||
import {
|
||||
IMMICH_ARCHIVE_COMPLETE,
|
||||
IMMICH_ARCHIVE_FILE_COUNT,
|
||||
IMMICH_CONTENT_LENGTH_HINT,
|
||||
} from '../../constants/download.constant';
|
||||
import { DownloadFilesDto } from './dto/download-files.dto';
|
||||
import { CreateAssetsShareLinkDto } from './dto/create-asset-shared-link.dto';
|
||||
import { SharedLinkResponseDto } from '@app/domain';
|
||||
@@ -59,8 +54,9 @@ import FileNotEmptyValidator from '../validation/file-not-empty-validator';
|
||||
import { RemoveAssetsDto } from '../album/dto/remove-assets.dto';
|
||||
import { AssetBulkUploadCheckDto } from './dto/asset-check.dto';
|
||||
import { AssetBulkUploadCheckResponseDto } from './response-dto/asset-check-response.dto';
|
||||
import { AssetIdDto } from './dto/asset-id.dto';
|
||||
import { UUIDParamDto } from '../../controllers/dto/uuid-param.dto';
|
||||
import { DeviceIdDto } from './dto/device-id.dto';
|
||||
import { handleDownload } from '../../app.utils';
|
||||
|
||||
function asStreamableFile({ stream, type, length }: ImmichReadStream) {
|
||||
return new StreamableFile(stream, { type, length });
|
||||
@@ -118,14 +114,14 @@ export class AssetController {
|
||||
}
|
||||
|
||||
@SharedLinkRoute()
|
||||
@Get('/download/:assetId')
|
||||
@Get('/download/:id')
|
||||
@ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } })
|
||||
async downloadFile(
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@Response({ passthrough: true }) res: Res,
|
||||
@Param() { assetId }: AssetIdDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
) {
|
||||
return this.assetService.downloadFile(authUser, assetId).then(asStreamableFile);
|
||||
return this.assetService.downloadFile(authUser, id).then(asStreamableFile);
|
||||
}
|
||||
|
||||
@SharedLinkRoute()
|
||||
@@ -138,12 +134,7 @@ export class AssetController {
|
||||
) {
|
||||
this.assetService.checkDownloadAccess(authUser);
|
||||
await this.assetService.checkAssetsAccess(authUser, [...dto.assetIds]);
|
||||
const { stream, fileName, fileSize, fileCount, complete } = await this.assetService.downloadFiles(dto);
|
||||
res.attachment(fileName);
|
||||
res.setHeader(IMMICH_CONTENT_LENGTH_HINT, fileSize);
|
||||
res.setHeader(IMMICH_ARCHIVE_FILE_COUNT, fileCount);
|
||||
res.setHeader(IMMICH_ARCHIVE_COMPLETE, `${complete}`);
|
||||
return stream;
|
||||
return this.assetService.downloadFiles(dto).then((download) => handleDownload(download, res));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,16 +149,11 @@ export class AssetController {
|
||||
@Response({ passthrough: true }) res: Res,
|
||||
) {
|
||||
this.assetService.checkDownloadAccess(authUser);
|
||||
const { stream, fileName, fileSize, fileCount, complete } = await this.assetService.downloadLibrary(authUser, dto);
|
||||
res.attachment(fileName);
|
||||
res.setHeader(IMMICH_CONTENT_LENGTH_HINT, fileSize);
|
||||
res.setHeader(IMMICH_ARCHIVE_FILE_COUNT, fileCount);
|
||||
res.setHeader(IMMICH_ARCHIVE_COMPLETE, `${complete}`);
|
||||
return stream;
|
||||
return this.assetService.downloadLibrary(authUser, dto).then((download) => handleDownload(download, res));
|
||||
}
|
||||
|
||||
@SharedLinkRoute()
|
||||
@Get('/file/:assetId')
|
||||
@Get('/file/:id')
|
||||
@Header('Cache-Control', 'max-age=31536000')
|
||||
@ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } })
|
||||
async serveFile(
|
||||
@@ -175,25 +161,25 @@ export class AssetController {
|
||||
@Headers() headers: Record<string, string>,
|
||||
@Response({ passthrough: true }) res: Res,
|
||||
@Query(new ValidationPipe({ transform: true })) query: ServeFileDto,
|
||||
@Param() { assetId }: AssetIdDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
) {
|
||||
await this.assetService.checkAssetsAccess(authUser, [assetId]);
|
||||
return this.assetService.serveFile(authUser, assetId, query, res, headers);
|
||||
await this.assetService.checkAssetsAccess(authUser, [id]);
|
||||
return this.assetService.serveFile(authUser, id, query, res, headers);
|
||||
}
|
||||
|
||||
@SharedLinkRoute()
|
||||
@Get('/thumbnail/:assetId')
|
||||
@Get('/thumbnail/:id')
|
||||
@Header('Cache-Control', 'max-age=31536000')
|
||||
@ApiOkResponse({ content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } })
|
||||
async getAssetThumbnail(
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@Headers() headers: Record<string, string>,
|
||||
@Response({ passthrough: true }) res: Res,
|
||||
@Param() { assetId }: AssetIdDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@Query(new ValidationPipe({ transform: true })) query: GetAssetThumbnailDto,
|
||||
) {
|
||||
await this.assetService.checkAssetsAccess(authUser, [assetId]);
|
||||
return this.assetService.getAssetThumbnail(assetId, query, res, headers);
|
||||
await this.assetService.checkAssetsAccess(authUser, [id]);
|
||||
return this.assetService.getAssetThumbnail(id, query, res, headers);
|
||||
}
|
||||
|
||||
@Get('/curated-objects')
|
||||
@@ -273,26 +259,23 @@ export class AssetController {
|
||||
* Get a single asset's information
|
||||
*/
|
||||
@SharedLinkRoute()
|
||||
@Get('/assetById/:assetId')
|
||||
async getAssetById(
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@Param() { assetId }: AssetIdDto,
|
||||
): Promise<AssetResponseDto> {
|
||||
await this.assetService.checkAssetsAccess(authUser, [assetId]);
|
||||
return await this.assetService.getAssetById(authUser, assetId);
|
||||
@Get('/assetById/:id')
|
||||
async getAssetById(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<AssetResponseDto> {
|
||||
await this.assetService.checkAssetsAccess(authUser, [id]);
|
||||
return await this.assetService.getAssetById(authUser, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an asset
|
||||
*/
|
||||
@Put('/:assetId')
|
||||
@Put('/:id')
|
||||
async updateAsset(
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@Param() { assetId }: AssetIdDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@Body(ValidationPipe) dto: UpdateAssetDto,
|
||||
): Promise<AssetResponseDto> {
|
||||
await this.assetService.checkAssetsAccess(authUser, [assetId], true);
|
||||
return await this.assetService.updateAsset(authUser, assetId, dto);
|
||||
await this.assetService.checkAssetsAccess(authUser, [id], true);
|
||||
return await this.assetService.updateAsset(authUser, id, dto);
|
||||
}
|
||||
|
||||
@Delete('/')
|
||||
|
||||
@@ -5,7 +5,6 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { AssetEntity, ExifEntity } from '@app/infra/entities';
|
||||
import { AssetRepository, IAssetRepository } from './asset-repository';
|
||||
import { DownloadModule } from '../../modules/download/download.module';
|
||||
import { TagModule } from '../tag/tag.module';
|
||||
import { AlbumModule } from '../album/album.module';
|
||||
|
||||
const ASSET_REPOSITORY_PROVIDER = {
|
||||
@@ -18,7 +17,6 @@ const ASSET_REPOSITORY_PROVIDER = {
|
||||
//
|
||||
TypeOrmModule.forFeature([AssetEntity, ExifEntity]),
|
||||
DownloadModule,
|
||||
TagModule,
|
||||
AlbumModule,
|
||||
],
|
||||
controllers: [AssetController],
|
||||
|
||||
@@ -229,7 +229,7 @@ describe('AssetService', () => {
|
||||
|
||||
expect(assetRepositoryMock.getById).toHaveBeenCalledWith(asset1.id);
|
||||
expect(sharedLinkRepositoryMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
|
||||
expect(sharedLinkRepositoryMock.save).not.toHaveBeenCalled();
|
||||
expect(sharedLinkRepositoryMock.update).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should add assets to a shared link', async () => {
|
||||
@@ -241,13 +241,13 @@ describe('AssetService', () => {
|
||||
assetRepositoryMock.getById.mockResolvedValue(asset1);
|
||||
sharedLinkRepositoryMock.get.mockResolvedValue(sharedLinkStub.valid);
|
||||
sharedLinkRepositoryMock.hasAssetAccess.mockResolvedValue(true);
|
||||
sharedLinkRepositoryMock.save.mockResolvedValue(sharedLinkStub.valid);
|
||||
sharedLinkRepositoryMock.update.mockResolvedValue(sharedLinkStub.valid);
|
||||
|
||||
await expect(sut.addAssetsToSharedLink(authDto, dto)).resolves.toEqual(sharedLinkResponseStub.valid);
|
||||
|
||||
expect(assetRepositoryMock.getById).toHaveBeenCalledWith(asset1.id);
|
||||
expect(sharedLinkRepositoryMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
|
||||
expect(sharedLinkRepositoryMock.save).toHaveBeenCalled();
|
||||
expect(sharedLinkRepositoryMock.update).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should remove assets from a shared link', async () => {
|
||||
@@ -259,13 +259,13 @@ describe('AssetService', () => {
|
||||
assetRepositoryMock.getById.mockResolvedValue(asset1);
|
||||
sharedLinkRepositoryMock.get.mockResolvedValue(sharedLinkStub.valid);
|
||||
sharedLinkRepositoryMock.hasAssetAccess.mockResolvedValue(true);
|
||||
sharedLinkRepositoryMock.save.mockResolvedValue(sharedLinkStub.valid);
|
||||
sharedLinkRepositoryMock.update.mockResolvedValue(sharedLinkStub.valid);
|
||||
|
||||
await expect(sut.removeAssetsFromSharedLink(authDto, dto)).resolves.toEqual(sharedLinkResponseStub.valid);
|
||||
|
||||
expect(assetRepositoryMock.getById).toHaveBeenCalledWith(asset1.id);
|
||||
expect(sharedLinkRepositoryMock.get).toHaveBeenCalledWith(authDto.id, authDto.sharedLinkId);
|
||||
expect(sharedLinkRepositoryMock.save).toHaveBeenCalled();
|
||||
expect(sharedLinkRepositoryMock.update).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ import { ICryptoRepository, IJobRepository } from '@app/domain';
|
||||
import { DownloadService } from '../../modules/download/download.service';
|
||||
import { DownloadDto } from './dto/download-library.dto';
|
||||
import { IAlbumRepository } from '../album/album-repository';
|
||||
import { ShareCore } from '@app/domain';
|
||||
import { SharedLinkCore } from '@app/domain';
|
||||
import { IPartnerRepository } from '@app/domain';
|
||||
import { ISharedLinkRepository } from '@app/domain';
|
||||
import { DownloadFilesDto } from './dto/download-files.dto';
|
||||
@@ -80,7 +80,7 @@ interface ServableFile {
|
||||
@Injectable()
|
||||
export class AssetService {
|
||||
readonly logger = new Logger(AssetService.name);
|
||||
private shareCore: ShareCore;
|
||||
private shareCore: SharedLinkCore;
|
||||
private assetCore: AssetCore;
|
||||
private partnerCore: PartnerCore;
|
||||
|
||||
@@ -97,7 +97,7 @@ export class AssetService {
|
||||
@Inject(IPartnerRepository) private partnerRepository: IPartnerRepository,
|
||||
) {
|
||||
this.assetCore = new AssetCore(_assetRepository, jobRepository);
|
||||
this.shareCore = new ShareCore(sharedLinkRepository, cryptoRepository);
|
||||
this.shareCore = new SharedLinkCore(sharedLinkRepository, cryptoRepository);
|
||||
this.partnerCore = new PartnerCore(partnerRepository);
|
||||
}
|
||||
|
||||
@@ -533,7 +533,7 @@ export class AssetService {
|
||||
|
||||
const result = await this._assetRepository.getAssetCountByTimeBucket(
|
||||
getAssetCountByTimeBucketDto.userId || authUser.id,
|
||||
getAssetCountByTimeBucketDto.timeGroup,
|
||||
getAssetCountByTimeBucketDto,
|
||||
);
|
||||
|
||||
return mapAssetCountByTimeBucket(result);
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsUUID } from 'class-validator';
|
||||
|
||||
export class AssetIdDto {
|
||||
@IsNotEmpty()
|
||||
@IsUUID('4')
|
||||
@ApiProperty({ format: 'uuid' })
|
||||
assetId!: string;
|
||||
}
|
||||
@@ -16,6 +16,14 @@ export class AssetSearchDto {
|
||||
@Transform(toBoolean)
|
||||
isArchived?: boolean;
|
||||
|
||||
/**
|
||||
* Include assets without thumbnails
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@Transform(toBoolean)
|
||||
withoutThumbs?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@IsNumber()
|
||||
skip?: number;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsNotEmpty, IsOptional, IsUUID } from 'class-validator';
|
||||
import { Transform } from 'class-transformer';
|
||||
import { IsBoolean, IsNotEmpty, IsOptional, IsUUID } from 'class-validator';
|
||||
import { toBoolean } from '../../../utils/transform.util';
|
||||
|
||||
export enum TimeGroupEnum {
|
||||
Day = 'day',
|
||||
@@ -19,4 +21,12 @@ export class GetAssetCountByTimeBucketDto {
|
||||
@IsUUID('4')
|
||||
@ApiProperty({ format: 'uuid' })
|
||||
userId?: string;
|
||||
|
||||
/**
|
||||
* Include assets without thumbnails
|
||||
*/
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@Transform(toBoolean)
|
||||
withoutThumbs?: boolean;
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { IsOptional, IsString } from 'class-validator';
|
||||
|
||||
export class UpdateTagDto {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
name?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
renameTagId?: string;
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
import { Controller, Get, Post, Body, Patch, Param, Delete, ValidationPipe } from '@nestjs/common';
|
||||
import { TagService } from './tag.service';
|
||||
import { CreateTagDto } from './dto/create-tag.dto';
|
||||
import { UpdateTagDto } from './dto/update-tag.dto';
|
||||
import { Authenticated } from '../../decorators/authenticated.decorator';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { AuthUserDto, GetAuthUser } from '../../decorators/auth-user.decorator';
|
||||
import { mapTag, TagResponseDto } from '@app/domain';
|
||||
import { UUIDParamDto } from '../../controllers/dto/uuid-param.dto';
|
||||
|
||||
@ApiTags('Tag')
|
||||
@Controller('tag')
|
||||
@Authenticated()
|
||||
export class TagController {
|
||||
constructor(private readonly tagService: TagService) {}
|
||||
|
||||
@Post()
|
||||
create(
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@Body(ValidationPipe) createTagDto: CreateTagDto,
|
||||
): Promise<TagResponseDto> {
|
||||
return this.tagService.create(authUser, createTagDto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll(@GetAuthUser() authUser: AuthUserDto): Promise<TagResponseDto[]> {
|
||||
return this.tagService.findAll(authUser);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async findOne(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<TagResponseDto> {
|
||||
const tag = await this.tagService.findOne(authUser, id);
|
||||
return mapTag(tag);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
update(
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@Body(ValidationPipe) updateTagDto: UpdateTagDto,
|
||||
): Promise<TagResponseDto> {
|
||||
return this.tagService.update(authUser, id, updateTagDto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
delete(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> {
|
||||
return this.tagService.remove(authUser, id);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TagService } from './tag.service';
|
||||
import { TagController } from './tag.controller';
|
||||
import { TagEntity } from '@app/infra/entities';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { TagRepository, ITagRepository } from './tag.repository';
|
||||
|
||||
const TAG_REPOSITORY_PROVIDER = {
|
||||
provide: ITagRepository,
|
||||
useClass: TagRepository,
|
||||
};
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([TagEntity])],
|
||||
controllers: [TagController],
|
||||
providers: [TagService, TAG_REPOSITORY_PROVIDER],
|
||||
exports: [TAG_REPOSITORY_PROVIDER],
|
||||
})
|
||||
export class TagModule {}
|
||||
@@ -1,61 +0,0 @@
|
||||
import { TagEntity, TagType } from '@app/infra/entities';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { In, Repository } from 'typeorm';
|
||||
import { UpdateTagDto } from './dto/update-tag.dto';
|
||||
|
||||
export interface ITagRepository {
|
||||
create(userId: string, tagType: TagType, tagName: string): Promise<TagEntity>;
|
||||
getByIds(userId: string, tagIds: string[]): Promise<TagEntity[]>;
|
||||
getById(tagId: string, userId: string): Promise<TagEntity | null>;
|
||||
getByUserId(userId: string): Promise<TagEntity[]>;
|
||||
update(tag: TagEntity, updateTagDto: UpdateTagDto): Promise<TagEntity | null>;
|
||||
remove(tag: TagEntity): Promise<TagEntity>;
|
||||
}
|
||||
|
||||
export const ITagRepository = 'ITagRepository';
|
||||
|
||||
@Injectable()
|
||||
export class TagRepository implements ITagRepository {
|
||||
constructor(
|
||||
@InjectRepository(TagEntity)
|
||||
private tagRepository: Repository<TagEntity>,
|
||||
) {}
|
||||
|
||||
async create(userId: string, tagType: TagType, tagName: string): Promise<TagEntity> {
|
||||
const tag = new TagEntity();
|
||||
tag.name = tagName;
|
||||
tag.type = tagType;
|
||||
tag.userId = userId;
|
||||
|
||||
return this.tagRepository.save(tag);
|
||||
}
|
||||
|
||||
async getById(tagId: string, userId: string): Promise<TagEntity | null> {
|
||||
return await this.tagRepository.findOne({ where: { id: tagId, userId }, relations: ['user'] });
|
||||
}
|
||||
|
||||
async getByIds(userId: string, tagIds: string[]): Promise<TagEntity[]> {
|
||||
return await this.tagRepository.find({
|
||||
where: { id: In(tagIds), userId },
|
||||
relations: {
|
||||
user: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async getByUserId(userId: string): Promise<TagEntity[]> {
|
||||
return await this.tagRepository.find({ where: { userId } });
|
||||
}
|
||||
|
||||
async update(tag: TagEntity, updateTagDto: UpdateTagDto): Promise<TagEntity> {
|
||||
tag.name = updateTagDto.name ?? tag.name;
|
||||
tag.renameTagId = updateTagDto.renameTagId ?? tag.renameTagId;
|
||||
|
||||
return this.tagRepository.save(tag);
|
||||
}
|
||||
|
||||
async remove(tag: TagEntity): Promise<TagEntity> {
|
||||
return await this.tagRepository.remove(tag);
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
import { TagEntity, TagType, UserEntity } from '@app/infra/entities';
|
||||
import { AuthUserDto } from '../../decorators/auth-user.decorator';
|
||||
import { ITagRepository } from './tag.repository';
|
||||
import { TagService } from './tag.service';
|
||||
|
||||
describe('TagService', () => {
|
||||
let sut: TagService;
|
||||
let tagRepositoryMock: jest.Mocked<ITagRepository>;
|
||||
|
||||
const user1AuthUser: AuthUserDto = Object.freeze({
|
||||
id: '1111',
|
||||
email: 'testuser@email.com',
|
||||
isAdmin: false,
|
||||
});
|
||||
|
||||
const user1: UserEntity = Object.freeze({
|
||||
id: '1111',
|
||||
firstName: 'Alex',
|
||||
lastName: 'Tran',
|
||||
isAdmin: true,
|
||||
email: 'testuser@email.com',
|
||||
profileImagePath: '',
|
||||
shouldChangePassword: true,
|
||||
createdAt: new Date('2022-12-02T19:29:23.603Z'),
|
||||
deletedAt: null,
|
||||
updatedAt: new Date('2022-12-02T19:29:23.603Z'),
|
||||
tags: [],
|
||||
assets: [],
|
||||
oauthId: 'oauth-id-1',
|
||||
storageLabel: null,
|
||||
});
|
||||
|
||||
// const user2: UserEntity = Object.freeze({
|
||||
// id: '2222',
|
||||
// firstName: 'Alex',
|
||||
// lastName: 'Tran',
|
||||
// isAdmin: true,
|
||||
// email: 'testuser2@email.com',
|
||||
// profileImagePath: '',
|
||||
// shouldChangePassword: true,
|
||||
// createdAt: '2022-12-02T19:29:23.603Z',
|
||||
// deletedAt: undefined,
|
||||
// tags: [],
|
||||
// oauthId: 'oauth-id-2',
|
||||
// });
|
||||
|
||||
const user1Tag1: TagEntity = Object.freeze({
|
||||
name: 'user 1 tag 1',
|
||||
type: TagType.CUSTOM,
|
||||
userId: user1.id,
|
||||
user: user1,
|
||||
renameTagId: '',
|
||||
id: 'user1-tag-1-id',
|
||||
assets: [],
|
||||
});
|
||||
|
||||
// const user1Tag2: TagEntity = Object.freeze({
|
||||
// name: 'user 1 tag 2',
|
||||
// type: TagType.CUSTOM,
|
||||
// userId: user1.id,
|
||||
// user: user1,
|
||||
// renameTagId: '',
|
||||
// id: 'user1-tag-2-id',
|
||||
// assets: [],
|
||||
// });
|
||||
|
||||
beforeAll(() => {
|
||||
tagRepositoryMock = {
|
||||
create: jest.fn(),
|
||||
getByIds: jest.fn(),
|
||||
getById: jest.fn(),
|
||||
getByUserId: jest.fn(),
|
||||
remove: jest.fn(),
|
||||
update: jest.fn(),
|
||||
};
|
||||
|
||||
sut = new TagService(tagRepositoryMock);
|
||||
});
|
||||
|
||||
it('creates tag', async () => {
|
||||
const createTagDto = {
|
||||
name: 'user 1 tag 1',
|
||||
type: TagType.CUSTOM,
|
||||
};
|
||||
|
||||
tagRepositoryMock.create.mockResolvedValue(user1Tag1);
|
||||
|
||||
const result = await sut.create(user1AuthUser, createTagDto);
|
||||
|
||||
expect(result.userId).toEqual(user1AuthUser.id);
|
||||
expect(result.name).toEqual(createTagDto.name);
|
||||
expect(result.type).toEqual(createTagDto.type);
|
||||
});
|
||||
});
|
||||
@@ -1,52 +0,0 @@
|
||||
import { TagEntity } from '@app/infra/entities';
|
||||
import { BadRequestException, Inject, Injectable, Logger } from '@nestjs/common';
|
||||
import { AuthUserDto } from '../../decorators/auth-user.decorator';
|
||||
import { CreateTagDto } from './dto/create-tag.dto';
|
||||
import { UpdateTagDto } from './dto/update-tag.dto';
|
||||
import { ITagRepository } from './tag.repository';
|
||||
import { mapTag, TagResponseDto } from '@app/domain';
|
||||
|
||||
@Injectable()
|
||||
export class TagService {
|
||||
readonly logger = new Logger(TagService.name);
|
||||
|
||||
constructor(@Inject(ITagRepository) private _tagRepository: ITagRepository) {}
|
||||
|
||||
async create(authUser: AuthUserDto, createTagDto: CreateTagDto) {
|
||||
try {
|
||||
const newTag = await this._tagRepository.create(authUser.id, createTagDto.type, createTagDto.name);
|
||||
return mapTag(newTag);
|
||||
} catch (e: any) {
|
||||
this.logger.error(e, e.stack);
|
||||
throw new BadRequestException(`Failed to create tag: ${e.detail}`);
|
||||
}
|
||||
}
|
||||
|
||||
async findAll(authUser: AuthUserDto) {
|
||||
const tags = await this._tagRepository.getByUserId(authUser.id);
|
||||
return tags.map(mapTag);
|
||||
}
|
||||
|
||||
async findOne(authUser: AuthUserDto, id: string): Promise<TagEntity> {
|
||||
const tag = await this._tagRepository.getById(id, authUser.id);
|
||||
|
||||
if (!tag) {
|
||||
throw new BadRequestException('Tag not found');
|
||||
}
|
||||
|
||||
return tag;
|
||||
}
|
||||
|
||||
async update(authUser: AuthUserDto, id: string, updateTagDto: UpdateTagDto): Promise<TagResponseDto> {
|
||||
const tag = await this.findOne(authUser, id);
|
||||
|
||||
await this._tagRepository.update(tag, updateTagDto);
|
||||
|
||||
return mapTag(tag);
|
||||
}
|
||||
|
||||
async remove(authUser: AuthUserDto, id: string): Promise<void> {
|
||||
const tag = await this.findOne(authUser, id);
|
||||
await this._tagRepository.remove(tag);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { JobService } from '@app/domain';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Cron, CronExpression } from '@nestjs/schedule';
|
||||
|
||||
@Injectable()
|
||||
export class AppCronJobs {
|
||||
constructor(private jobService: JobService) {}
|
||||
|
||||
@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
|
||||
async onNightlyJob() {
|
||||
await this.jobService.handleNightlyJobs();
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,37 @@
|
||||
import { Module, OnModuleInit } from '@nestjs/common';
|
||||
import { AssetModule } from './api-v1/asset/asset.module';
|
||||
import { AlbumModule } from './api-v1/album/album.module';
|
||||
import { AppController } from './app.controller';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
import { TagModule } from './api-v1/tag/tag.module';
|
||||
import { DomainModule, SearchService } from '@app/domain';
|
||||
import { DomainModule } from '@app/domain';
|
||||
import { InfraModule } from '@app/infra';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
import { AlbumModule } from './api-v1/album/album.module';
|
||||
import { AssetModule } from './api-v1/asset/asset.module';
|
||||
import { AppService } from './app.service';
|
||||
import {
|
||||
AlbumController,
|
||||
APIKeyController,
|
||||
AppController,
|
||||
AssetController,
|
||||
AuthController,
|
||||
PersonController,
|
||||
JobController,
|
||||
OAuthController,
|
||||
PartnerController,
|
||||
PersonController,
|
||||
SearchController,
|
||||
ServerInfoController,
|
||||
SharedLinkController,
|
||||
SystemConfigController,
|
||||
TagController,
|
||||
UserController,
|
||||
} from './controllers';
|
||||
import { APP_GUARD } from '@nestjs/core';
|
||||
import { AuthGuard } from './middlewares/auth.guard';
|
||||
import { AppCronJobs } from './app.cron-jobs';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
//
|
||||
DomainModule.register({ imports: [InfraModule] }),
|
||||
AssetModule,
|
||||
AlbumModule,
|
||||
ScheduleModule.forRoot(),
|
||||
TagModule,
|
||||
],
|
||||
controllers: [
|
||||
AppController,
|
||||
@@ -46,6 +46,7 @@ import { AppCronJobs } from './app.cron-jobs';
|
||||
ServerInfoController,
|
||||
SharedLinkController,
|
||||
SystemConfigController,
|
||||
TagController,
|
||||
UserController,
|
||||
PersonController,
|
||||
],
|
||||
@@ -53,12 +54,7 @@ import { AppCronJobs } from './app.cron-jobs';
|
||||
//
|
||||
{ provide: APP_GUARD, useExisting: AuthGuard },
|
||||
AuthGuard,
|
||||
AppCronJobs,
|
||||
AppService,
|
||||
],
|
||||
})
|
||||
export class AppModule implements OnModuleInit {
|
||||
constructor(private searchService: SearchService) {}
|
||||
async onModuleInit() {
|
||||
await this.searchService.bootstrap();
|
||||
}
|
||||
}
|
||||
export class AppModule {}
|
||||
|
||||
22
server/apps/immich/src/app.service.ts
Normal file
22
server/apps/immich/src/app.service.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { JobService, SearchService, StorageService } from '@app/domain';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Cron, CronExpression } from '@nestjs/schedule';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
constructor(
|
||||
private jobService: JobService,
|
||||
private searchService: SearchService,
|
||||
private storageService: StorageService,
|
||||
) {}
|
||||
|
||||
@Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT)
|
||||
async onNightlyJob() {
|
||||
await this.jobService.handleNightlyJobs();
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.storageService.init();
|
||||
await this.searchService.init();
|
||||
}
|
||||
}
|
||||
101
server/apps/immich/src/app.utils.ts
Normal file
101
server/apps/immich/src/app.utils.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import { IMMICH_ACCESS_COOKIE, IMMICH_API_KEY_HEADER, IMMICH_API_KEY_NAME, SERVER_VERSION } from '@app/domain';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import {
|
||||
DocumentBuilder,
|
||||
OpenAPIObject,
|
||||
SwaggerCustomOptions,
|
||||
SwaggerDocumentOptions,
|
||||
SwaggerModule,
|
||||
} from '@nestjs/swagger';
|
||||
import { Response } from 'express';
|
||||
import { writeFileSync } from 'fs';
|
||||
import path from 'path';
|
||||
import { Metadata } from './decorators/authenticated.decorator';
|
||||
import { DownloadArchive } from './modules/download/download.service';
|
||||
|
||||
export const handleDownload = (download: DownloadArchive, res: Response) => {
|
||||
res.attachment(download.fileName);
|
||||
res.setHeader('X-Immich-Content-Length-Hint', download.fileSize);
|
||||
res.setHeader('X-Immich-Archive-File-Count', download.fileCount);
|
||||
res.setHeader('X-Immich-Archive-Complete', `${download.complete}`);
|
||||
return download.stream;
|
||||
};
|
||||
|
||||
const patchOpenAPI = (document: OpenAPIObject) => {
|
||||
for (const path of Object.values(document.paths)) {
|
||||
const operations = {
|
||||
get: path.get,
|
||||
put: path.put,
|
||||
post: path.post,
|
||||
delete: path.delete,
|
||||
options: path.options,
|
||||
head: path.head,
|
||||
patch: path.patch,
|
||||
trace: path.trace,
|
||||
};
|
||||
|
||||
for (const operation of Object.values(operations)) {
|
||||
if (!operation) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((operation.security || []).find((item) => !!item[Metadata.PUBLIC_SECURITY])) {
|
||||
delete operation.security;
|
||||
}
|
||||
|
||||
if (operation.summary === '') {
|
||||
delete operation.summary;
|
||||
}
|
||||
|
||||
if (operation.description === '') {
|
||||
delete operation.description;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return document;
|
||||
};
|
||||
|
||||
export const useSwagger = (app: INestApplication, isDev: boolean) => {
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Immich')
|
||||
.setDescription('Immich API')
|
||||
.setVersion(SERVER_VERSION)
|
||||
.addBearerAuth({
|
||||
type: 'http',
|
||||
scheme: 'Bearer',
|
||||
in: 'header',
|
||||
})
|
||||
.addCookieAuth(IMMICH_ACCESS_COOKIE)
|
||||
.addApiKey(
|
||||
{
|
||||
type: 'apiKey',
|
||||
in: 'header',
|
||||
name: IMMICH_API_KEY_HEADER,
|
||||
},
|
||||
IMMICH_API_KEY_NAME,
|
||||
)
|
||||
.addServer('/api')
|
||||
.build();
|
||||
|
||||
const options: SwaggerDocumentOptions = {
|
||||
operationIdFactory: (controllerKey: string, methodKey: string) => methodKey,
|
||||
};
|
||||
|
||||
const doc = SwaggerModule.createDocument(app, config, options);
|
||||
|
||||
const customOptions: SwaggerCustomOptions = {
|
||||
swaggerOptions: {
|
||||
persistAuthorization: true,
|
||||
},
|
||||
customSiteTitle: 'Immich API Documentation',
|
||||
};
|
||||
|
||||
SwaggerModule.setup('doc', app, doc, customOptions);
|
||||
|
||||
if (isDev) {
|
||||
// Generate API Documentation only in development mode
|
||||
const outputPath = path.resolve(process.cwd(), 'immich-openapi-specs.json');
|
||||
writeFileSync(outputPath, JSON.stringify(patchOpenAPI(doc), null, 2), { encoding: 'utf8' });
|
||||
}
|
||||
};
|
||||
@@ -1,3 +0,0 @@
|
||||
export const IMMICH_CONTENT_LENGTH_HINT = 'X-Immich-Content-Length-Hint';
|
||||
export const IMMICH_ARCHIVE_FILE_COUNT = 'X-Immich-Archive-File-Count';
|
||||
export const IMMICH_ARCHIVE_COMPLETE = 'X-Immich-Archive-Complete';
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from './album.controller';
|
||||
export * from './api-key.controller';
|
||||
export * from './app.controller';
|
||||
export * from './asset.controller';
|
||||
export * from './auth.controller';
|
||||
export * from './job.controller';
|
||||
@@ -10,4 +11,5 @@ export * from './search.controller';
|
||||
export * from './server-info.controller';
|
||||
export * from './shared-link.controller';
|
||||
export * from './system-config.controller';
|
||||
export * from './tag.controller';
|
||||
export * from './user.controller';
|
||||
|
||||
@@ -19,6 +19,6 @@ export class JobController {
|
||||
@Put('/:jobId')
|
||||
async sendJobCommand(@Param() { jobId }: JobIdDto, @Body() dto: JobCommandDto): Promise<JobStatusDto> {
|
||||
await this.service.handleCommand(jobId, dto);
|
||||
return await this.service.getJobStatus(jobId);
|
||||
return this.service.getJobStatus(jobId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { AuthUserDto, EditSharedLinkDto, SharedLinkResponseDto, ShareService } from '@app/domain';
|
||||
import { AuthUserDto, EditSharedLinkDto, SharedLinkResponseDto, SharedLinkService } from '@app/domain';
|
||||
import { Body, Controller, Delete, Get, Param, Patch } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { GetAuthUser } from '../decorators/auth-user.decorator';
|
||||
@@ -11,7 +11,7 @@ import { UUIDParamDto } from './dto/uuid-param.dto';
|
||||
@Authenticated()
|
||||
@UseValidation()
|
||||
export class SharedLinkController {
|
||||
constructor(private readonly service: ShareService) {}
|
||||
constructor(private readonly service: SharedLinkService) {}
|
||||
|
||||
@Get()
|
||||
getAllSharedLinks(@GetAuthUser() authUser: AuthUserDto): Promise<SharedLinkResponseDto[]> {
|
||||
@@ -29,20 +29,20 @@ export class SharedLinkController {
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
): Promise<SharedLinkResponseDto> {
|
||||
return this.service.getById(authUser, id, true);
|
||||
return this.service.get(authUser, id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
updateSharedLink(
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@Body() dto: EditSharedLinkDto,
|
||||
): Promise<SharedLinkResponseDto> {
|
||||
return this.service.update(authUser, id, dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
removeSharedLink(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> {
|
||||
return this.service.remove(authUser, id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
editSharedLink(
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@Body() dto: EditSharedLinkDto,
|
||||
): Promise<SharedLinkResponseDto> {
|
||||
return this.service.edit(authUser, id, dto);
|
||||
}
|
||||
}
|
||||
|
||||
75
server/apps/immich/src/controllers/tag.controller.ts
Normal file
75
server/apps/immich/src/controllers/tag.controller.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import {
|
||||
AssetIdsDto,
|
||||
AssetIdsResponseDto,
|
||||
AssetResponseDto,
|
||||
CreateTagDto,
|
||||
TagResponseDto,
|
||||
TagService,
|
||||
UpdateTagDto,
|
||||
} from '@app/domain';
|
||||
import { Body, Controller, Delete, Get, Param, Patch, Post, Put } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { AuthUserDto, GetAuthUser } from '../decorators/auth-user.decorator';
|
||||
import { Authenticated } from '../decorators/authenticated.decorator';
|
||||
import { UseValidation } from '../decorators/use-validation.decorator';
|
||||
import { UUIDParamDto } from './dto/uuid-param.dto';
|
||||
|
||||
@ApiTags('Tag')
|
||||
@Controller('tag')
|
||||
@Authenticated()
|
||||
@UseValidation()
|
||||
export class TagController {
|
||||
constructor(private service: TagService) {}
|
||||
|
||||
@Post()
|
||||
createTag(@GetAuthUser() authUser: AuthUserDto, @Body() dto: CreateTagDto): Promise<TagResponseDto> {
|
||||
return this.service.create(authUser, dto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
getAllTags(@GetAuthUser() authUser: AuthUserDto): Promise<TagResponseDto[]> {
|
||||
return this.service.getAll(authUser);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
getTagById(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<TagResponseDto> {
|
||||
return this.service.getById(authUser, id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
updateTag(
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@Body() dto: UpdateTagDto,
|
||||
): Promise<TagResponseDto> {
|
||||
return this.service.update(authUser, id, dto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
deleteTag(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<void> {
|
||||
return this.service.remove(authUser, id);
|
||||
}
|
||||
|
||||
@Get(':id/assets')
|
||||
getTagAssets(@GetAuthUser() authUser: AuthUserDto, @Param() { id }: UUIDParamDto): Promise<AssetResponseDto[]> {
|
||||
return this.service.getAssets(authUser, id);
|
||||
}
|
||||
|
||||
@Put(':id/assets')
|
||||
tagAssets(
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
@Body() dto: AssetIdsDto,
|
||||
): Promise<AssetIdsResponseDto[]> {
|
||||
return this.service.addAssets(authUser, id, dto);
|
||||
}
|
||||
|
||||
@Delete(':id/assets')
|
||||
untagAssets(
|
||||
@GetAuthUser() authUser: AuthUserDto,
|
||||
@Body() dto: AssetIdsDto,
|
||||
@Param() { id }: UUIDParamDto,
|
||||
): Promise<AssetIdsResponseDto[]> {
|
||||
return this.service.removeAssets(authUser, id, dto);
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,17 @@
|
||||
import {
|
||||
getLogLevels,
|
||||
IMMICH_ACCESS_COOKIE,
|
||||
IMMICH_API_KEY_HEADER,
|
||||
IMMICH_API_KEY_NAME,
|
||||
MACHINE_LEARNING_ENABLED,
|
||||
SearchService,
|
||||
SERVER_VERSION,
|
||||
StorageService,
|
||||
} from '@app/domain';
|
||||
import { getLogLevels, MACHINE_LEARNING_ENABLED, SearchService, SERVER_VERSION } from '@app/domain';
|
||||
import { RedisIoAdapter } from '@app/infra';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
import { DocumentBuilder, SwaggerDocumentOptions, SwaggerModule } from '@nestjs/swagger';
|
||||
import { json } from 'body-parser';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import { writeFileSync } from 'fs';
|
||||
import path from 'path';
|
||||
import { AppModule } from './app.module';
|
||||
import { patchOpenAPI } from './utils/patch-open-api.util';
|
||||
import { AppService } from './app.service';
|
||||
import { useSwagger } from './app.utils';
|
||||
|
||||
const logger = new Logger('ImmichServer');
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
const serverPort = Number(process.env.SERVER_PORT) || 3001;
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
|
||||
@@ -31,57 +22,14 @@ async function bootstrap() {
|
||||
app.set('etag', 'strong');
|
||||
app.use(cookieParser());
|
||||
app.use(json({ limit: '10mb' }));
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
if (isDev) {
|
||||
app.enableCors();
|
||||
}
|
||||
|
||||
const serverPort = Number(process.env.SERVER_PORT) || 3001;
|
||||
|
||||
app.useWebSocketAdapter(new RedisIoAdapter(app));
|
||||
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Immich')
|
||||
.setDescription('Immich API')
|
||||
.setVersion(SERVER_VERSION)
|
||||
.addBearerAuth({
|
||||
type: 'http',
|
||||
scheme: 'Bearer',
|
||||
in: 'header',
|
||||
})
|
||||
.addCookieAuth(IMMICH_ACCESS_COOKIE)
|
||||
.addApiKey(
|
||||
{
|
||||
type: 'apiKey',
|
||||
in: 'header',
|
||||
name: IMMICH_API_KEY_HEADER,
|
||||
},
|
||||
IMMICH_API_KEY_NAME,
|
||||
)
|
||||
.addServer('/api')
|
||||
.build();
|
||||
|
||||
const apiDocumentOptions: SwaggerDocumentOptions = {
|
||||
operationIdFactory: (controllerKey: string, methodKey: string) => methodKey,
|
||||
};
|
||||
|
||||
const apiDocument = SwaggerModule.createDocument(app, config, apiDocumentOptions);
|
||||
|
||||
SwaggerModule.setup('doc', app, apiDocument, {
|
||||
swaggerOptions: {
|
||||
persistAuthorization: true,
|
||||
},
|
||||
customSiteTitle: 'Immich API Documentation',
|
||||
});
|
||||
|
||||
app.get(StorageService).init();
|
||||
useSwagger(app, isDev);
|
||||
await app.get(AppService).init();
|
||||
|
||||
await app.listen(serverPort, () => {
|
||||
if (process.env.NODE_ENV == 'development') {
|
||||
// Generate API Documentation only in development mode
|
||||
const outputPath = path.resolve(process.cwd(), 'immich-openapi-specs.json');
|
||||
writeFileSync(outputPath, JSON.stringify(patchOpenAPI(apiDocument), null, 2), { encoding: 'utf8' });
|
||||
}
|
||||
|
||||
const envName = (process.env.NODE_ENV || 'development').toUpperCase();
|
||||
logger.log(
|
||||
`Running Immich Server in ${envName} environment - version ${SERVER_VERSION} - Listening on port: ${serverPort}`,
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
import { OpenAPIObject } from '@nestjs/swagger';
|
||||
import { Metadata } from '../decorators/authenticated.decorator';
|
||||
|
||||
export function patchOpenAPI(document: OpenAPIObject) {
|
||||
for (const path of Object.values(document.paths)) {
|
||||
const operations = {
|
||||
get: path.get,
|
||||
put: path.put,
|
||||
post: path.post,
|
||||
delete: path.delete,
|
||||
options: path.options,
|
||||
head: path.head,
|
||||
patch: path.patch,
|
||||
trace: path.trace,
|
||||
};
|
||||
|
||||
for (const operation of Object.values(operations)) {
|
||||
if (!operation) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((operation.security || []).find((item) => !!item[Metadata.PUBLIC_SECURITY])) {
|
||||
delete operation.security;
|
||||
}
|
||||
|
||||
if (operation.summary === '') {
|
||||
delete operation.summary;
|
||||
}
|
||||
|
||||
if (operation.description === '') {
|
||||
delete operation.description;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return document;
|
||||
}
|
||||
75
server/apps/microservices/src/app.service.ts
Normal file
75
server/apps/microservices/src/app.service.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
import {
|
||||
FacialRecognitionService,
|
||||
IDeleteFilesJob,
|
||||
JobName,
|
||||
JobService,
|
||||
MediaService,
|
||||
MetadataService,
|
||||
PersonService,
|
||||
SearchService,
|
||||
SmartInfoService,
|
||||
StorageService,
|
||||
StorageTemplateService,
|
||||
SystemConfigService,
|
||||
UserService,
|
||||
} from '@app/domain';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { MetadataExtractionProcessor } from './processors/metadata-extraction.processor';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
constructor(
|
||||
// TODO refactor to domain
|
||||
private metadataProcessor: MetadataExtractionProcessor,
|
||||
|
||||
private facialRecognitionService: FacialRecognitionService,
|
||||
private jobService: JobService,
|
||||
private mediaService: MediaService,
|
||||
private metadataService: MetadataService,
|
||||
private personService: PersonService,
|
||||
private searchService: SearchService,
|
||||
private smartInfoService: SmartInfoService,
|
||||
private storageTemplateService: StorageTemplateService,
|
||||
private storageService: StorageService,
|
||||
private systemConfigService: SystemConfigService,
|
||||
private userService: UserService,
|
||||
) {}
|
||||
|
||||
async init() {
|
||||
await this.jobService.registerHandlers({
|
||||
[JobName.DELETE_FILES]: (data: IDeleteFilesJob) => this.storageService.handleDeleteFiles(data),
|
||||
[JobName.USER_DELETE_CHECK]: () => this.userService.handleUserDeleteCheck(),
|
||||
[JobName.USER_DELETION]: (data) => this.userService.handleUserDelete(data),
|
||||
[JobName.QUEUE_OBJECT_TAGGING]: (data) => this.smartInfoService.handleQueueObjectTagging(data),
|
||||
[JobName.CLASSIFY_IMAGE]: (data) => this.smartInfoService.handleClassifyImage(data),
|
||||
[JobName.QUEUE_ENCODE_CLIP]: (data) => this.smartInfoService.handleQueueEncodeClip(data),
|
||||
[JobName.ENCODE_CLIP]: (data) => this.smartInfoService.handleEncodeClip(data),
|
||||
[JobName.SEARCH_INDEX_ALBUMS]: () => this.searchService.handleIndexAlbums(),
|
||||
[JobName.SEARCH_INDEX_ASSETS]: () => this.searchService.handleIndexAssets(),
|
||||
[JobName.SEARCH_INDEX_FACES]: () => this.searchService.handleIndexFaces(),
|
||||
[JobName.SEARCH_INDEX_ALBUM]: (data) => this.searchService.handleIndexAlbum(data),
|
||||
[JobName.SEARCH_INDEX_ASSET]: (data) => this.searchService.handleIndexAsset(data),
|
||||
[JobName.SEARCH_INDEX_FACE]: (data) => this.searchService.handleIndexFace(data),
|
||||
[JobName.SEARCH_REMOVE_ALBUM]: (data) => this.searchService.handleRemoveAlbum(data),
|
||||
[JobName.SEARCH_REMOVE_ASSET]: (data) => this.searchService.handleRemoveAsset(data),
|
||||
[JobName.SEARCH_REMOVE_FACE]: (data) => this.searchService.handleRemoveFace(data),
|
||||
[JobName.STORAGE_TEMPLATE_MIGRATION]: () => this.storageTemplateService.handleMigration(),
|
||||
[JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE]: (data) => this.storageTemplateService.handleMigrationSingle(data),
|
||||
[JobName.SYSTEM_CONFIG_CHANGE]: () => this.systemConfigService.refreshConfig(),
|
||||
[JobName.QUEUE_GENERATE_THUMBNAILS]: (data) => this.mediaService.handleQueueGenerateThumbnails(data),
|
||||
[JobName.GENERATE_JPEG_THUMBNAIL]: (data) => this.mediaService.handleGenerateJpegThumbnail(data),
|
||||
[JobName.GENERATE_WEBP_THUMBNAIL]: (data) => this.mediaService.handleGenerateWepbThumbnail(data),
|
||||
[JobName.QUEUE_VIDEO_CONVERSION]: (data) => this.mediaService.handleQueueVideoConversion(data),
|
||||
[JobName.VIDEO_CONVERSION]: (data) => this.mediaService.handleVideoConversion(data),
|
||||
[JobName.QUEUE_METADATA_EXTRACTION]: (data) => this.metadataProcessor.handleQueueMetadataExtraction(data),
|
||||
[JobName.METADATA_EXTRACTION]: (data) => this.metadataProcessor.handleMetadataExtraction(data),
|
||||
[JobName.QUEUE_RECOGNIZE_FACES]: (data) => this.facialRecognitionService.handleQueueRecognizeFaces(data),
|
||||
[JobName.RECOGNIZE_FACES]: (data) => this.facialRecognitionService.handleRecognizeFaces(data),
|
||||
[JobName.GENERATE_FACE_THUMBNAIL]: (data) => this.facialRecognitionService.handleGenerateFaceThumbnail(data),
|
||||
[JobName.PERSON_CLEANUP]: () => this.personService.handlePersonCleanup(),
|
||||
[JobName.QUEUE_SIDECAR]: (data) => this.metadataService.handleQueueSidecar(data),
|
||||
[JobName.SIDECAR_DISCOVERY]: (data) => this.metadataService.handleSidecarDiscovery(data),
|
||||
[JobName.SIDECAR_SYNC]: () => this.metadataService.handleSidecarSync(),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@ import { getLogLevels, SERVER_VERSION } from '@app/domain';
|
||||
import { RedisIoAdapter } from '@app/infra';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppService } from './app.service';
|
||||
import { MicroservicesModule } from './microservices.module';
|
||||
import { ProcessorService } from './processor.service';
|
||||
import { MetadataExtractionProcessor } from './processors/metadata-extraction.processor';
|
||||
|
||||
const logger = new Logger('ImmichMicroservice');
|
||||
@@ -15,7 +15,7 @@ async function bootstrap() {
|
||||
|
||||
const listeningPort = Number(process.env.MICROSERVICES_PORT) || 3002;
|
||||
|
||||
await app.get(ProcessorService).init();
|
||||
await app.get(AppService).init();
|
||||
|
||||
app.useWebSocketAdapter(new RedisIoAdapter(app));
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { InfraModule } from '@app/infra';
|
||||
import { ExifEntity } from '@app/infra/entities';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ProcessorService } from './processor.service';
|
||||
import { AppService } from './app.service';
|
||||
import { MetadataExtractionProcessor } from './processors/metadata-extraction.processor';
|
||||
|
||||
@Module({
|
||||
@@ -12,6 +12,6 @@ import { MetadataExtractionProcessor } from './processors/metadata-extraction.pr
|
||||
DomainModule.register({ imports: [InfraModule] }),
|
||||
TypeOrmModule.forFeature([ExifEntity]),
|
||||
],
|
||||
providers: [MetadataExtractionProcessor, ProcessorService],
|
||||
providers: [MetadataExtractionProcessor, AppService],
|
||||
})
|
||||
export class MicroservicesModule {}
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
import {
|
||||
FacialRecognitionService,
|
||||
IDeleteFilesJob,
|
||||
JobItem,
|
||||
JobName,
|
||||
JobService,
|
||||
JOBS_TO_QUEUE,
|
||||
MediaService,
|
||||
MetadataService,
|
||||
PersonService,
|
||||
QueueName,
|
||||
QUEUE_TO_CONCURRENCY,
|
||||
SearchService,
|
||||
SmartInfoService,
|
||||
StorageService,
|
||||
StorageTemplateService,
|
||||
SystemConfigService,
|
||||
UserService,
|
||||
} from '@app/domain';
|
||||
import { getQueueToken } from '@nestjs/bull';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { ModuleRef } from '@nestjs/core';
|
||||
import { Queue } from 'bull';
|
||||
import { MetadataExtractionProcessor } from './processors/metadata-extraction.processor';
|
||||
|
||||
type JobHandler<T = any> = (data: T) => boolean | Promise<boolean>;
|
||||
|
||||
@Injectable()
|
||||
export class ProcessorService {
|
||||
constructor(
|
||||
private moduleRef: ModuleRef,
|
||||
// TODO refactor to domain
|
||||
private metadataProcessor: MetadataExtractionProcessor,
|
||||
|
||||
private facialRecognitionService: FacialRecognitionService,
|
||||
private jobService: JobService,
|
||||
private mediaService: MediaService,
|
||||
private metadataService: MetadataService,
|
||||
private personService: PersonService,
|
||||
private searchService: SearchService,
|
||||
private smartInfoService: SmartInfoService,
|
||||
private storageTemplateService: StorageTemplateService,
|
||||
private storageService: StorageService,
|
||||
private systemConfigService: SystemConfigService,
|
||||
private userService: UserService,
|
||||
) {}
|
||||
|
||||
private logger = new Logger(ProcessorService.name);
|
||||
|
||||
private handlers: Record<JobName, JobHandler> = {
|
||||
[JobName.DELETE_FILES]: (data: IDeleteFilesJob) => this.storageService.handleDeleteFiles(data),
|
||||
[JobName.USER_DELETE_CHECK]: () => this.userService.handleUserDeleteCheck(),
|
||||
[JobName.USER_DELETION]: (data) => this.userService.handleUserDelete(data),
|
||||
[JobName.QUEUE_OBJECT_TAGGING]: (data) => this.smartInfoService.handleQueueObjectTagging(data),
|
||||
[JobName.DETECT_OBJECTS]: (data) => this.smartInfoService.handleDetectObjects(data),
|
||||
[JobName.CLASSIFY_IMAGE]: (data) => this.smartInfoService.handleClassifyImage(data),
|
||||
[JobName.QUEUE_ENCODE_CLIP]: (data) => this.smartInfoService.handleQueueEncodeClip(data),
|
||||
[JobName.ENCODE_CLIP]: (data) => this.smartInfoService.handleEncodeClip(data),
|
||||
[JobName.SEARCH_INDEX_ALBUMS]: () => this.searchService.handleIndexAlbums(),
|
||||
[JobName.SEARCH_INDEX_ASSETS]: () => this.searchService.handleIndexAssets(),
|
||||
[JobName.SEARCH_INDEX_FACES]: () => this.searchService.handleIndexFaces(),
|
||||
[JobName.SEARCH_INDEX_ALBUM]: (data) => this.searchService.handleIndexAlbum(data),
|
||||
[JobName.SEARCH_INDEX_ASSET]: (data) => this.searchService.handleIndexAsset(data),
|
||||
[JobName.SEARCH_INDEX_FACE]: (data) => this.searchService.handleIndexFace(data),
|
||||
[JobName.SEARCH_REMOVE_ALBUM]: (data) => this.searchService.handleRemoveAlbum(data),
|
||||
[JobName.SEARCH_REMOVE_ASSET]: (data) => this.searchService.handleRemoveAsset(data),
|
||||
[JobName.SEARCH_REMOVE_FACE]: (data) => this.searchService.handleRemoveFace(data),
|
||||
[JobName.STORAGE_TEMPLATE_MIGRATION]: () => this.storageTemplateService.handleMigration(),
|
||||
[JobName.STORAGE_TEMPLATE_MIGRATION_SINGLE]: (data) => this.storageTemplateService.handleMigrationSingle(data),
|
||||
[JobName.SYSTEM_CONFIG_CHANGE]: () => this.systemConfigService.refreshConfig(),
|
||||
[JobName.QUEUE_GENERATE_THUMBNAILS]: (data) => this.mediaService.handleQueueGenerateThumbnails(data),
|
||||
[JobName.GENERATE_JPEG_THUMBNAIL]: (data) => this.mediaService.handleGenerateJpegThumbnail(data),
|
||||
[JobName.GENERATE_WEBP_THUMBNAIL]: (data) => this.mediaService.handleGenerateWepbThumbnail(data),
|
||||
[JobName.QUEUE_VIDEO_CONVERSION]: (data) => this.mediaService.handleQueueVideoConversion(data),
|
||||
[JobName.VIDEO_CONVERSION]: (data) => this.mediaService.handleVideoConversion(data),
|
||||
[JobName.QUEUE_METADATA_EXTRACTION]: (data) => this.metadataProcessor.handleQueueMetadataExtraction(data),
|
||||
[JobName.METADATA_EXTRACTION]: (data) => this.metadataProcessor.handleMetadataExtraction(data),
|
||||
[JobName.QUEUE_RECOGNIZE_FACES]: (data) => this.facialRecognitionService.handleQueueRecognizeFaces(data),
|
||||
[JobName.RECOGNIZE_FACES]: (data) => this.facialRecognitionService.handleRecognizeFaces(data),
|
||||
[JobName.GENERATE_FACE_THUMBNAIL]: (data) => this.facialRecognitionService.handleGenerateFaceThumbnail(data),
|
||||
[JobName.PERSON_CLEANUP]: () => this.personService.handlePersonCleanup(),
|
||||
[JobName.QUEUE_SIDECAR]: (data) => this.metadataService.handleQueueSidecar(data),
|
||||
[JobName.SIDECAR_DISCOVERY]: (data) => this.metadataService.handleSidecarDiscovery(data),
|
||||
[JobName.SIDECAR_SYNC]: () => this.metadataService.handleSidecarSync(),
|
||||
};
|
||||
|
||||
async init() {
|
||||
const queueSeen: Partial<Record<QueueName, boolean>> = {};
|
||||
|
||||
for (const jobName of Object.values(JobName)) {
|
||||
const handler = this.handlers[jobName];
|
||||
const queueName = JOBS_TO_QUEUE[jobName];
|
||||
const queue = this.moduleRef.get<Queue>(getQueueToken(queueName), { strict: false });
|
||||
|
||||
// only set concurrency on the first job for a queue, since concurrency stacks
|
||||
const seen = queueSeen[queueName];
|
||||
const concurrency = seen ? 0 : QUEUE_TO_CONCURRENCY[queueName];
|
||||
queueSeen[queueName] = true;
|
||||
|
||||
await queue.isReady();
|
||||
|
||||
queue.process(jobName, concurrency, async (job): Promise<void> => {
|
||||
try {
|
||||
const success = await handler(job.data);
|
||||
if (success) {
|
||||
await this.jobService.onDone({ name: jobName, data: job.data } as JobItem);
|
||||
}
|
||||
} catch (error: Error | any) {
|
||||
this.logger.error(`Unable to run job handler: ${error}`, error?.stack, job.data);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,13 +15,17 @@ import { Inject, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import tz_lookup from '@photostructure/tz-lookup';
|
||||
import { ExifDateTime, exiftool, Tags } from 'exiftool-vendored';
|
||||
import { exiftool, Tags } from 'exiftool-vendored';
|
||||
import ffmpeg, { FfprobeData } from 'fluent-ffmpeg';
|
||||
import { Duration } from 'luxon';
|
||||
import fs from 'node:fs';
|
||||
import sharp from 'sharp';
|
||||
import { Repository } from 'typeorm/repository/Repository';
|
||||
import { promisify } from 'util';
|
||||
import { parseLatitude, parseLongitude } from '../utils/exif/coordinates';
|
||||
import { exifTimeZone, exifToDate } from '../utils/exif/date-time';
|
||||
import { parseISO } from '../utils/exif/iso';
|
||||
import { toNumberOrNull } from '../utils/numbers';
|
||||
|
||||
const ffprobe = promisify<string, FfprobeData>(ffmpeg.ffprobe);
|
||||
|
||||
@@ -115,32 +119,13 @@ export class MetadataExtractionProcessor {
|
||||
})
|
||||
: {};
|
||||
|
||||
const exifToDate = (exifDate: string | Date | ExifDateTime | undefined) => {
|
||||
if (!exifDate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const date = exifDate instanceof ExifDateTime ? exifDate.toDate() : new Date(exifDate);
|
||||
if (isNaN(date.valueOf())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return date;
|
||||
};
|
||||
|
||||
const exifTimeZone = (exifDate: string | Date | ExifDateTime | undefined) => {
|
||||
const isExifDate = exifDate instanceof ExifDateTime;
|
||||
if (!isExifDate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return exifDate.zone ?? null;
|
||||
};
|
||||
|
||||
const getExifProperty = <T extends keyof ImmichTags>(...properties: T[]): any | null => {
|
||||
const getExifProperty = <T extends keyof ImmichTags>(
|
||||
...properties: T[]
|
||||
): NonNullable<ImmichTags[T]> | string | null => {
|
||||
for (const property of properties) {
|
||||
const value = sidecarExifData?.[property] ?? mediaExifData?.[property];
|
||||
if (value !== null && value !== undefined) {
|
||||
// Can also be string when the value cannot be parsed
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -159,25 +144,27 @@ export class MetadataExtractionProcessor {
|
||||
newExif.fileSizeInByte = fileSizeInBytes;
|
||||
newExif.make = getExifProperty('Make');
|
||||
newExif.model = getExifProperty('Model');
|
||||
newExif.exifImageHeight = getExifProperty('ExifImageHeight', 'ImageHeight');
|
||||
newExif.exifImageWidth = getExifProperty('ExifImageWidth', 'ImageWidth');
|
||||
newExif.exifImageHeight = toNumberOrNull(getExifProperty('ExifImageHeight', 'ImageHeight'));
|
||||
newExif.exifImageWidth = toNumberOrNull(getExifProperty('ExifImageWidth', 'ImageWidth'));
|
||||
newExif.exposureTime = getExifProperty('ExposureTime');
|
||||
newExif.orientation = getExifProperty('Orientation')?.toString();
|
||||
newExif.orientation = getExifProperty('Orientation')?.toString() ?? null;
|
||||
newExif.dateTimeOriginal = fileCreatedAt;
|
||||
newExif.modifyDate = fileModifiedAt;
|
||||
newExif.timeZone = timeZone;
|
||||
newExif.lensModel = getExifProperty('LensModel');
|
||||
newExif.fNumber = getExifProperty('FNumber');
|
||||
const focalLength = getExifProperty('FocalLength');
|
||||
newExif.focalLength = focalLength ? parseFloat(focalLength) : null;
|
||||
// This is unusual - exifData.ISO should return a number, but experienced that sidecar XMP
|
||||
// files MAY return an array of numbers instead.
|
||||
const iso = getExifProperty('ISO');
|
||||
newExif.iso = Array.isArray(iso) ? iso[0] : iso || null;
|
||||
newExif.latitude = getExifProperty('GPSLatitude');
|
||||
newExif.longitude = getExifProperty('GPSLongitude');
|
||||
newExif.livePhotoCID = getExifProperty('MediaGroupUUID');
|
||||
newExif.fNumber = toNumberOrNull(getExifProperty('FNumber'));
|
||||
newExif.focalLength = toNumberOrNull(getExifProperty('FocalLength'));
|
||||
|
||||
// Handle array values by converting to string
|
||||
const iso = getExifProperty('ISO')?.toString();
|
||||
newExif.iso = iso ? parseISO(iso) : null;
|
||||
|
||||
const latitude = getExifProperty('GPSLatitude');
|
||||
const longitude = getExifProperty('GPSLongitude');
|
||||
newExif.latitude = latitude !== null ? parseLatitude(latitude) : null;
|
||||
newExif.longitude = longitude !== null ? parseLongitude(longitude) : null;
|
||||
|
||||
newExif.livePhotoCID = getExifProperty('MediaGroupUUID');
|
||||
if (newExif.livePhotoCID && !asset.livePhotoVideoId) {
|
||||
const motionAsset = await this.assetRepository.findLivePhotoMatch({
|
||||
livePhotoCID: newExif.livePhotoCID,
|
||||
@@ -274,8 +261,8 @@ export class MetadataExtractionProcessor {
|
||||
const match = location.match(locationRegex);
|
||||
|
||||
if (match?.length === 3) {
|
||||
newExif.latitude = parseFloat(match[1]);
|
||||
newExif.longitude = parseFloat(match[2]);
|
||||
newExif.latitude = parseLatitude(match[1]);
|
||||
newExif.longitude = parseLongitude(match[2]);
|
||||
}
|
||||
} else if (videoTags && videoTags['com.apple.quicktime.location.ISO6709']) {
|
||||
const location = videoTags['com.apple.quicktime.location.ISO6709'] as string;
|
||||
@@ -283,8 +270,8 @@ export class MetadataExtractionProcessor {
|
||||
const match = location.match(locationRegex);
|
||||
|
||||
if (match?.length === 4) {
|
||||
newExif.latitude = parseFloat(match[1]);
|
||||
newExif.longitude = parseFloat(match[2]);
|
||||
newExif.latitude = parseLatitude(match[1]);
|
||||
newExif.longitude = parseLongitude(match[2]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
46
server/apps/microservices/src/utils/exif/coordinates.spec.ts
Normal file
46
server/apps/microservices/src/utils/exif/coordinates.spec.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { describe, it, expect } from '@jest/globals';
|
||||
import { parseLatitude, parseLongitude } from './coordinates';
|
||||
|
||||
describe('parsing latitude from string input', () => {
|
||||
it('returns null for invalid inputs', () => {
|
||||
expect(parseLatitude('')).toBeNull();
|
||||
expect(parseLatitude('NaN')).toBeNull();
|
||||
expect(parseLatitude('Infinity')).toBeNull();
|
||||
expect(parseLatitude('-Infinity')).toBeNull();
|
||||
expect(parseLatitude('90.001')).toBeNull();
|
||||
expect(parseLatitude(-90.000001)).toBeNull();
|
||||
expect(parseLatitude('1000')).toBeNull();
|
||||
expect(parseLatitude(-1000)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns the numeric coordinate for valid inputs', () => {
|
||||
expect(parseLatitude('90')).toBeCloseTo(90);
|
||||
expect(parseLatitude('-90')).toBeCloseTo(-90);
|
||||
expect(parseLatitude(89.999999)).toBeCloseTo(89.999999);
|
||||
expect(parseLatitude('-89.9')).toBeCloseTo(-89.9);
|
||||
expect(parseLatitude(0)).toBeCloseTo(0);
|
||||
expect(parseLatitude('-0.0')).toBeCloseTo(-0.0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parsing longitude from string input', () => {
|
||||
it('returns null for invalid inputs', () => {
|
||||
expect(parseLongitude('')).toBeNull();
|
||||
expect(parseLongitude('NaN')).toBeNull();
|
||||
expect(parseLongitude(Infinity)).toBeNull();
|
||||
expect(parseLongitude('-Infinity')).toBeNull();
|
||||
expect(parseLongitude('180.001')).toBeNull();
|
||||
expect(parseLongitude('-180.000001')).toBeNull();
|
||||
expect(parseLongitude(1000)).toBeNull();
|
||||
expect(parseLongitude('-1000')).toBeNull();
|
||||
});
|
||||
|
||||
it('returns the numeric coordinate for valid inputs', () => {
|
||||
expect(parseLongitude(180)).toBeCloseTo(180);
|
||||
expect(parseLongitude('-180')).toBeCloseTo(-180);
|
||||
expect(parseLongitude('179.999999')).toBeCloseTo(179.999999);
|
||||
expect(parseLongitude(-179.9)).toBeCloseTo(-179.9);
|
||||
expect(parseLongitude('0')).toBeCloseTo(0);
|
||||
expect(parseLongitude('-0.0')).toBeCloseTo(-0.0);
|
||||
});
|
||||
});
|
||||
19
server/apps/microservices/src/utils/exif/coordinates.ts
Normal file
19
server/apps/microservices/src/utils/exif/coordinates.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { isNumberInRange } from '../numbers';
|
||||
|
||||
export function parseLatitude(input: string | number): number | null {
|
||||
const latitude = typeof input === 'string' ? Number.parseFloat(input) : input;
|
||||
|
||||
if (isNumberInRange(latitude, -90, 90)) {
|
||||
return latitude;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function parseLongitude(input: string | number): number | null {
|
||||
const longitude = typeof input === 'string' ? Number.parseFloat(input) : input;
|
||||
|
||||
if (isNumberInRange(longitude, -180, 180)) {
|
||||
return longitude;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
36
server/apps/microservices/src/utils/exif/date-time.spec.ts
Normal file
36
server/apps/microservices/src/utils/exif/date-time.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { describe, expect, it } from '@jest/globals';
|
||||
import { ExifDateTime } from 'exiftool-vendored';
|
||||
import { exifTimeZone, exifToDate } from './date-time';
|
||||
|
||||
describe('converts exif date to JS date', () => {
|
||||
it('returns null for invalid inputs', () => {
|
||||
expect(exifToDate(undefined)).toBeNull();
|
||||
expect(exifToDate('invalid')).toBeNull();
|
||||
expect(exifToDate(new Date('invalid'))).toBeNull();
|
||||
expect(exifToDate(ExifDateTime.fromEXIF('invalid'))).toBeNull();
|
||||
});
|
||||
|
||||
it('returns a valid date object for valid inputs', () => {
|
||||
const date = new Date('2023');
|
||||
expect(exifToDate(date)).toBeInstanceOf(Date);
|
||||
expect(exifToDate(date)?.toISOString()).toBe('2023-01-01T00:00:00.000Z');
|
||||
expect(exifToDate('2023')).toBeInstanceOf(Date);
|
||||
|
||||
const exifDateTime = ExifDateTime.fromISO('2023-01-01T00:00:00.000Z');
|
||||
expect(exifToDate(exifDateTime)).toBeInstanceOf(Date);
|
||||
expect(exifToDate(exifDateTime)?.toISOString()).toBe('2023-01-01T00:00:00.000Z');
|
||||
});
|
||||
});
|
||||
|
||||
describe('extracts the timezone from a date', () => {
|
||||
it('returns null for invalid inputs', () => {
|
||||
expect(exifTimeZone(undefined)).toBeNull();
|
||||
expect(exifTimeZone('')).toBeNull();
|
||||
expect(exifTimeZone(new Date('2023'))).toBeNull();
|
||||
expect(exifTimeZone(ExifDateTime.fromEXIF('invalid'))).toBeNull();
|
||||
});
|
||||
|
||||
it('returns the timezone for valid inputs', () => {
|
||||
expect(exifTimeZone(ExifDateTime.fromEXIF('2020:12:29 14:24:45.700-05:00'))).toBe('UTC-5');
|
||||
});
|
||||
});
|
||||
24
server/apps/microservices/src/utils/exif/date-time.ts
Normal file
24
server/apps/microservices/src/utils/exif/date-time.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ExifDateTime } from 'exiftool-vendored';
|
||||
import { isDecimalNumber } from '../numbers';
|
||||
|
||||
export function exifToDate(exifDate: string | Date | ExifDateTime | undefined): Date | null {
|
||||
if (!exifDate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const date = exifDate instanceof ExifDateTime ? exifDate.toDate() : new Date(exifDate);
|
||||
if (!isDecimalNumber(date.valueOf())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
export function exifTimeZone(exifDate: string | Date | ExifDateTime | undefined): string | null {
|
||||
const isExifDate = exifDate instanceof ExifDateTime;
|
||||
if (!isExifDate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return exifDate.zone ?? null;
|
||||
}
|
||||
24
server/apps/microservices/src/utils/exif/iso.spec.ts
Normal file
24
server/apps/microservices/src/utils/exif/iso.spec.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { describe, it, expect } from '@jest/globals';
|
||||
import { parseISO } from './iso';
|
||||
|
||||
describe('parsing ISO values', () => {
|
||||
it('returns null for invalid values', () => {
|
||||
expect(parseISO('')).toBeNull();
|
||||
expect(parseISO(',,,')).toBeNull();
|
||||
expect(parseISO('invalid')).toBeNull();
|
||||
expect(parseISO('-5')).toBeNull();
|
||||
expect(parseISO('99999999999999')).toBeNull();
|
||||
});
|
||||
|
||||
it('returns the ISO number for valid inputs', () => {
|
||||
expect(parseISO('0.0')).toBe(0);
|
||||
expect(parseISO('32000.9')).toBe(32000);
|
||||
});
|
||||
|
||||
it('returns the first valid ISO number in a comma separated list', () => {
|
||||
expect(parseISO('400, 200, 100')).toBe(400);
|
||||
expect(parseISO('-1600,800')).toBe(800);
|
||||
expect(parseISO('-1, a., 1200')).toBe(1200);
|
||||
expect(parseISO('NaN,50,100')).toBe(50);
|
||||
});
|
||||
});
|
||||
14
server/apps/microservices/src/utils/exif/iso.ts
Normal file
14
server/apps/microservices/src/utils/exif/iso.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { isNumberInRange } from '../numbers';
|
||||
|
||||
export function parseISO(input: string): number | null {
|
||||
const values = input.split(',');
|
||||
|
||||
for (const value of values) {
|
||||
const iso = Number.parseInt(value, 10);
|
||||
if (isNumberInRange(iso, 0, 2 ** 32)) {
|
||||
return iso;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
47
server/apps/microservices/src/utils/numbers.spec.ts
Normal file
47
server/apps/microservices/src/utils/numbers.spec.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { describe, it, expect } from '@jest/globals';
|
||||
import { isDecimalNumber, isNumberInRange, toNumberOrNull } from './numbers';
|
||||
|
||||
describe('checks if a number is a decimal number', () => {
|
||||
it('returns false for non-decimal numbers', () => {
|
||||
expect(isDecimalNumber(NaN)).toBe(false);
|
||||
expect(isDecimalNumber(Infinity)).toBe(false);
|
||||
expect(isDecimalNumber(-Infinity)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true for decimal numbers', () => {
|
||||
expect(isDecimalNumber(0)).toBe(true);
|
||||
expect(isDecimalNumber(-0)).toBe(true);
|
||||
expect(isDecimalNumber(10.12345)).toBe(true);
|
||||
expect(isDecimalNumber(Number.MAX_VALUE)).toBe(true);
|
||||
expect(isDecimalNumber(Number.MIN_VALUE)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('checks if a number is within a range', () => {
|
||||
it('returns false for numbers outside the range', () => {
|
||||
expect(isNumberInRange(0, 10, 10)).toBe(false);
|
||||
expect(isNumberInRange(0.01, 10, 10)).toBe(false);
|
||||
expect(isNumberInRange(50.1, 0, 50)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true for numbers inside the range', () => {
|
||||
expect(isNumberInRange(0, 0, 50)).toBe(true);
|
||||
expect(isNumberInRange(50, 0, 50)).toBe(true);
|
||||
expect(isNumberInRange(-50.12345, -50.12345, 0)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('converts input to a number or null', () => {
|
||||
it('returns null for invalid inputs', () => {
|
||||
expect(toNumberOrNull(null)).toBeNull();
|
||||
expect(toNumberOrNull(undefined)).toBeNull();
|
||||
expect(toNumberOrNull('')).toBeNull();
|
||||
expect(toNumberOrNull(NaN)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns a number for valid inputs', () => {
|
||||
expect(toNumberOrNull(0)).toBeCloseTo(0);
|
||||
expect(toNumberOrNull('0')).toBeCloseTo(0);
|
||||
expect(toNumberOrNull('-123.45')).toBeCloseTo(-123.45);
|
||||
});
|
||||
});
|
||||
19
server/apps/microservices/src/utils/numbers.ts
Normal file
19
server/apps/microservices/src/utils/numbers.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export function isDecimalNumber(num: number): boolean {
|
||||
return !Number.isNaN(num) && Number.isFinite(num);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if `num` is a valid number and is between `start` and `end` (inclusive)
|
||||
*/
|
||||
export function isNumberInRange(num: number, start: number, end: number): boolean {
|
||||
return isDecimalNumber(num) && num >= start && num <= end;
|
||||
}
|
||||
|
||||
export function toNumberOrNull(input: number | string | null | undefined): number | null {
|
||||
if (input === null || input === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const num = typeof input === 'string' ? Number.parseFloat(input) : input;
|
||||
return isDecimalNumber(num) ? num : null;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
6
server/libs/domain/src/asset/dto/asset-ids.dto.ts
Normal file
6
server/libs/domain/src/asset/dto/asset-ids.dto.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { ValidateUUID } from '../../../../../apps/immich/src/decorators/validate-uuid.decorator';
|
||||
|
||||
export class AssetIdsDto {
|
||||
@ValidateUUID({ each: true })
|
||||
assetIds!: string[];
|
||||
}
|
||||
2
server/libs/domain/src/asset/dto/index.ts
Normal file
2
server/libs/domain/src/asset/dto/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './asset-ids.dto';
|
||||
export * from './map-marker.dto';
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { toBoolean } from 'apps/immich/src/utils/transform.util';
|
||||
import { Transform, Type } from 'class-transformer';
|
||||
import { IsBoolean, IsDate, IsOptional } from 'class-validator';
|
||||
import { toBoolean } from '../../../../../apps/immich/src/utils/transform.util';
|
||||
|
||||
export class MapMarkerDto {
|
||||
@ApiProperty()
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './asset.repository';
|
||||
export * from './asset.service';
|
||||
export * from './dto';
|
||||
export * from './response-dto';
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
export enum AssetIdErrorReason {
|
||||
DUPLICATE = 'duplicate',
|
||||
NO_PERMISSION = 'no_permission',
|
||||
NOT_FOUND = 'not_found',
|
||||
}
|
||||
|
||||
export class AssetIdsResponseDto {
|
||||
assetId!: string;
|
||||
success!: boolean;
|
||||
error?: AssetIdErrorReason;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
export * from './asset-ids-response.dto';
|
||||
export * from './asset-response.dto';
|
||||
export * from './exif-response.dto';
|
||||
export * from './smart-info-response.dto';
|
||||
export * from './map-marker-response.dto';
|
||||
export * from './smart-info-response.dto';
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
} from '../../test';
|
||||
import { IKeyRepository } from '../api-key';
|
||||
import { ICryptoRepository } from '../crypto/crypto.repository';
|
||||
import { ISharedLinkRepository } from '../share';
|
||||
import { ISharedLinkRepository } from '../shared-link';
|
||||
import { ISystemConfigRepository } from '../system-config';
|
||||
import { IUserRepository } from '../user';
|
||||
import { IUserTokenRepository } from '../user-token';
|
||||
@@ -277,11 +277,20 @@ describe('AuthService', () => {
|
||||
await expect(sut.validate(headers, {})).rejects.toBeInstanceOf(UnauthorizedException);
|
||||
});
|
||||
|
||||
it('should accept a valid key', async () => {
|
||||
it('should accept a base64url key', async () => {
|
||||
shareMock.getByKey.mockResolvedValue(sharedLinkStub.valid);
|
||||
userMock.get.mockResolvedValue(userEntityStub.admin);
|
||||
const headers: IncomingHttpHeaders = { 'x-immich-share-key': 'key' };
|
||||
const headers: IncomingHttpHeaders = { 'x-immich-share-key': sharedLinkStub.valid.key.toString('base64url') };
|
||||
await expect(sut.validate(headers, {})).resolves.toEqual(authStub.adminSharedLink);
|
||||
expect(shareMock.getByKey).toHaveBeenCalledWith(sharedLinkStub.valid.key);
|
||||
});
|
||||
|
||||
it('should accept a hex key', async () => {
|
||||
shareMock.getByKey.mockResolvedValue(sharedLinkStub.valid);
|
||||
userMock.get.mockResolvedValue(userEntityStub.admin);
|
||||
const headers: IncomingHttpHeaders = { 'x-immich-share-key': sharedLinkStub.valid.key.toString('hex') };
|
||||
await expect(sut.validate(headers, {})).resolves.toEqual(authStub.adminSharedLink);
|
||||
expect(shareMock.getByKey).toHaveBeenCalledWith(sharedLinkStub.valid.key);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import { AuthUserDto, ChangePasswordDto, LoginCredentialDto, SignUpDto } from '.
|
||||
import { AdminSignupResponseDto, LoginResponseDto, LogoutResponseDto, mapAdminSignupResponse } from './response-dto';
|
||||
import { IUserTokenRepository, UserTokenCore } from '../user-token';
|
||||
import cookieParser from 'cookie';
|
||||
import { ISharedLinkRepository, ShareCore } from '../share';
|
||||
import { ISharedLinkRepository, SharedLinkCore } from '../shared-link';
|
||||
import { APIKeyCore } from '../api-key/api-key.core';
|
||||
import { IKeyRepository } from '../api-key';
|
||||
import { AuthDeviceResponseDto, mapUserToken } from './response-dto';
|
||||
@@ -29,7 +29,7 @@ export class AuthService {
|
||||
private authCore: AuthCore;
|
||||
private oauthCore: OAuthCore;
|
||||
private userCore: UserCore;
|
||||
private shareCore: ShareCore;
|
||||
private shareCore: SharedLinkCore;
|
||||
private keyCore: APIKeyCore;
|
||||
|
||||
private logger = new Logger(AuthService.name);
|
||||
@@ -48,7 +48,7 @@ export class AuthService {
|
||||
this.authCore = new AuthCore(cryptoRepository, configRepository, userTokenRepository, initialConfig);
|
||||
this.oauthCore = new OAuthCore(configRepository, initialConfig);
|
||||
this.userCore = new UserCore(userRepository, cryptoRepository);
|
||||
this.shareCore = new ShareCore(shareRepository, cryptoRepository);
|
||||
this.shareCore = new SharedLinkCore(shareRepository, cryptoRepository);
|
||||
this.keyCore = new APIKeyCore(cryptoRepository, keyRepository);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,11 +12,12 @@ import { PartnerService } from './partner';
|
||||
import { PersonService } from './person';
|
||||
import { SearchService } from './search';
|
||||
import { ServerInfoService } from './server-info';
|
||||
import { ShareService } from './share';
|
||||
import { SharedLinkService } from './shared-link';
|
||||
import { SmartInfoService } from './smart-info';
|
||||
import { StorageService } from './storage';
|
||||
import { StorageTemplateService } from './storage-template';
|
||||
import { INITIAL_SYSTEM_CONFIG, SystemConfigService } from './system-config';
|
||||
import { TagService } from './tag';
|
||||
import { UserService } from './user';
|
||||
|
||||
const providers: Provider[] = [
|
||||
@@ -33,11 +34,12 @@ const providers: Provider[] = [
|
||||
PartnerService,
|
||||
SearchService,
|
||||
ServerInfoService,
|
||||
ShareService,
|
||||
SharedLinkService,
|
||||
SmartInfoService,
|
||||
StorageService,
|
||||
StorageTemplateService,
|
||||
SystemConfigService,
|
||||
TagService,
|
||||
UserService,
|
||||
{
|
||||
provide: INITIAL_SYSTEM_CONFIG,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user