Compare commits

..

20 Commits

Author SHA1 Message Date
Alex Tran 65b865ad08 Merge branch 'main' of github.com:immich-app/immich into refactor/immich-image-provider 2024-02-13 15:31:37 -06:00
Marty Fuhry 21ea5d8d85 linter 2024-02-13 08:57:07 -05:00
Marty Fuhry 17b6e0250a Got rid of usePreview for local image providers since we always show a thumbnail anyway first 2024-02-13 08:31:10 -05:00
Marty Fuhry a033d751b3 Moves http client to global private place for reuse 2024-02-12 18:20:31 -05:00
martyfuhry ea293dfe06 fix(mobile): Multipart image loading for iOS double swipe (#7064)
* uses local thumb first

* Multipart thumbnail

* Clean up file delete

* await file delete

* Fynn's comments, made thumbnail smaller and doesn't crash on erroring out on thumbnail

* lint

---------

Co-authored-by: Marty Fuhry <marty@fuhry.farm>
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-02-12 16:09:45 -06:00
Alex aea7651c75 Use big thumbnail for local image on ios 2024-02-12 15:09:13 -06:00
Marty Fuhry 74ff8f1e56 Forgot one thumbhash removal 2024-02-12 13:48:03 -05:00
Marty Fuhry c544526400 removes thumbhash support for now 2024-02-12 13:31:01 -05:00
Marty Fuhry 2f8cb30c34 fixes thumbnails, removes unused values, adds better thumbnail size 2024-02-12 13:21:57 -05:00
Marty Fuhry 4140a66cab Uses octoimage for fade in and placeholders 2024-02-12 10:48:48 -05:00
Marty Fuhry bf20c363fd Merge branch 'refactor/immich-image-provider' of github.com:immich-app/immich into refactor/immich-image-provider 2024-02-12 09:08:19 -05:00
shenlong e270ae0017 feat(mobile): thumbhash support (#7028)
* feat(mobile): thumbhash support

* perf(mobile): store bmp thumbhash bytes in Isar

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
2024-02-12 09:07:42 -05:00
Marty Fuhry e2a25742f4 Fixes for thumbnails 2024-02-11 16:45:38 -05:00
Marty Fuhry 61e32f4bf7 Removed OriginalImageProvider 2024-02-10 15:07:40 -05:00
Marty Fuhry 02075bc52e First draft of the immich image provider, working nicely! 2024-02-10 11:06:32 -05:00
Marty Fuhry 230c7dae0f Immich provider used in gallery 2024-02-10 10:09:10 -05:00
Marty Fuhry 0e672a5b24 wip everything but activity asset thumbnail needs some help with a remote id 2024-02-10 08:49:57 -05:00
Marty Fuhry c1452a359c wip load preview 2024-02-10 06:19:16 -05:00
Marty Fuhry 758b5cd6c2 uses image provider 2024-02-09 21:13:24 -05:00
Marty Fuhry 28413fedcc Adds image provider 2024-02-09 21:13:20 -05:00
278 changed files with 5089 additions and 9217 deletions
+3 -3
View File
@@ -285,7 +285,7 @@ jobs:
- name: Run API generation
run: make open-api
- name: Find file changes
uses: tj-actions/verify-changed-files@v18
uses: tj-actions/verify-changed-files@v13.1
id: verify-changed-files
with:
files: |
@@ -340,7 +340,7 @@ jobs:
run: npm run typeorm:migrations:generate ./src/infra/migrations/TestMigration
- name: Find file changes
uses: tj-actions/verify-changed-files@v18
uses: tj-actions/verify-changed-files@v13.1
id: verify-changed-files
with:
files: |
@@ -358,7 +358,7 @@ jobs:
DB_URL: postgres://postgres:postgres@localhost:5432/immich
- name: Find file changes
uses: tj-actions/verify-changed-files@v18
uses: tj-actions/verify-changed-files@v13.1
id: verify-changed-sql-files
with:
files: |
+1 -3
View File
@@ -2,7 +2,5 @@
"singleQuote": true,
"trailingComma": "all",
"printWidth": 120,
"semi": true,
"plugins": ["prettier-plugin-organize-imports"],
"organizeImportsSkipDestructiveCodeActions": true
"semi": true
}
+15 -42
View File
@@ -31,8 +31,6 @@
"glob": "^10.3.1",
"immich": "file:../server",
"mock-fs": "^5.2.0",
"prettier": "^3.2.5",
"prettier-plugin-organize-imports": "^3.2.4",
"typescript": "^5.3.3",
"vite": "^5.0.12",
"vitest": "^1.2.2",
@@ -1307,9 +1305,9 @@
}
},
"node_modules/@types/node": {
"version": "20.11.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz",
"integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==",
"version": "20.11.16",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz",
"integrity": "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
@@ -4070,10 +4068,11 @@
}
},
"node_modules/prettier": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
"integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
"integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
"dev": true,
"peer": true,
"bin": {
"prettier": "bin/prettier.cjs"
},
@@ -4096,26 +4095,6 @@
"node": ">=6.0.0"
}
},
"node_modules/prettier-plugin-organize-imports": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.4.tgz",
"integrity": "sha512-6m8WBhIp0dfwu0SkgfOxJqh+HpdyfqSSLfKKRZSFbDuEQXDDndb8fTpRWkUrX/uBenkex3MgnVk0J3b3Y5byog==",
"dev": true,
"peerDependencies": {
"@volar/vue-language-plugin-pug": "^1.0.4",
"@volar/vue-typescript": "^1.0.4",
"prettier": ">=2.0",
"typescript": ">=2.9"
},
"peerDependenciesMeta": {
"@volar/vue-language-plugin-pug": {
"optional": true
},
"@volar/vue-typescript": {
"optional": true
}
}
},
"node_modules/pretty-format": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
@@ -6447,9 +6426,9 @@
}
},
"@types/node": {
"version": "20.11.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz",
"integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==",
"version": "20.11.16",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz",
"integrity": "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==",
"dev": true,
"requires": {
"undici-types": "~5.26.4"
@@ -8564,10 +8543,11 @@
"dev": true
},
"prettier": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz",
"integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==",
"dev": true
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz",
"integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==",
"dev": true,
"peer": true
},
"prettier-linter-helpers": {
"version": "1.0.0",
@@ -8578,13 +8558,6 @@
"fast-diff": "^1.1.2"
}
},
"prettier-plugin-organize-imports": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.4.tgz",
"integrity": "sha512-6m8WBhIp0dfwu0SkgfOxJqh+HpdyfqSSLfKKRZSFbDuEQXDDndb8fTpRWkUrX/uBenkex3MgnVk0J3b3Y5byog==",
"dev": true,
"requires": {}
},
"pretty-format": {
"version": "29.7.0",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
-2
View File
@@ -32,8 +32,6 @@
"glob": "^10.3.1",
"immich": "file:../server",
"mock-fs": "^5.2.0",
"prettier": "^3.2.5",
"prettier-plugin-organize-imports": "^3.2.4",
"typescript": "^5.3.3",
"vite": "^5.0.12",
"vitest": "^1.2.2",
+1 -1
View File
@@ -1,6 +1,6 @@
import { ServerVersionResponseDto, UserResponseDto } from '@immich/sdk';
import { ImmichApi } from 'src/services/api.service';
import { SessionService } from '../services/session.service';
import { ImmichApi } from 'src/services/api.service';
export abstract class BaseCommand {
protected sessionService!: SessionService;
+5 -5
View File
@@ -1,13 +1,13 @@
import byteSize from 'byte-size';
import cliProgress from 'cli-progress';
import { createHash } from 'node:crypto';
import fs, { createReadStream } from 'node:fs';
import { access, constants, stat, unlink } from 'node:fs/promises';
import os from 'node:os';
import { basename } from 'node:path';
import { ImmichApi } from 'src/services/api.service';
import { CrawlService } from '../services/crawl.service';
import { BaseCommand } from './base-command';
import { basename } from 'node:path';
import { access, constants, stat, unlink } from 'node:fs/promises';
import { createHash } from 'node:crypto';
import os from 'node:os';
import { ImmichApi } from 'src/services/api.service';
class Asset {
readonly path: string;
+1 -1
View File
@@ -1,7 +1,7 @@
#! /usr/bin/env node
import { Command, Option } from 'commander';
import os from 'node:os';
import path from 'node:path';
import os from 'node:os';
import { version } from '../package.json';
import { LoginCommand } from './commands/login.command';
import { LogoutCommand } from './commands/logout.command';
+7 -7
View File
@@ -1,11 +1,4 @@
import {
ApiKeyCreateDto,
AssetBulkUploadCheckDto,
BulkIdsDto,
CreateAlbumDto,
CreateAssetDto,
LoginCredentialDto,
SignUpDto,
addAssetsToAlbum,
checkBulkUpload,
createAlbum,
@@ -20,6 +13,13 @@ import {
pingServer,
signUpAdmin,
uploadFile,
ApiKeyCreateDto,
AssetBulkUploadCheckDto,
BulkIdsDto,
CreateAlbumDto,
CreateAssetDto,
LoginCredentialDto,
SignUpDto,
} from '@immich/sdk';
/**
+1 -1
View File
@@ -1,5 +1,5 @@
import mockfs from 'mock-fs';
import { CrawlOptions, CrawlService } from './crawl.service';
import { CrawlService, CrawlOptions } from './crawl.service';
interface Test {
test: string;
+1 -1
View File
@@ -1,3 +1,4 @@
import { SessionService } from './session.service';
import fs from 'node:fs';
import yaml from 'yaml';
import {
@@ -10,7 +11,6 @@ import {
readTestAuthFile,
spyOnConsole,
} from '../../test/cli-test-utils';
import { SessionService } from './session.service';
const mocks = vi.hoisted(() => {
return {
+2 -2
View File
@@ -1,8 +1,8 @@
import { restoreTempFolder, testApp } from '@test-utils';
import { readFile, stat } from 'node:fs/promises';
import { CLI_BASE_OPTIONS, TEST_AUTH_FILE, deleteAuthFile, setup, spyOnConsole } from 'test/cli-test-utils';
import yaml from 'yaml';
import { readFile, stat } from 'node:fs/promises';
import { LoginCommand } from '../../src/commands/login.command';
import yaml from 'yaml';
describe(`login-key (e2e)`, () => {
let apiKey: string;
+1 -1
View File
@@ -1,6 +1,6 @@
import path from 'node:path';
import { PostgreSqlContainer } from '@testcontainers/postgresql';
import { access } from 'node:fs/promises';
import path from 'node:path';
export const directoryExists = (directory: string) =>
access(directory)
+1 -1
View File
@@ -1,7 +1,7 @@
import { IMMICH_TEST_ASSET_PATH, restoreTempFolder, testApp } from '@test-utils';
import { ImmichApi } from 'src/services/api.service';
import { CLI_BASE_OPTIONS, setup, spyOnConsole } from 'test/cli-test-utils';
import { UploadCommand } from '../../src/commands/upload.command';
import { ImmichApi } from 'src/services/api.service';
describe(`upload (e2e)`, () => {
let api: ImmichApi;
+1 -1
View File
@@ -57,7 +57,7 @@ services:
image: immich-web-dev:latest
build:
context: ../web
command: [ "/usr/src/app/bin/immich-web" ]
command: "/usr/src/app/bin/immich-web"
env_file:
- .env
ports:
+1 -1
View File
@@ -2,7 +2,7 @@ import { test, expect } from '@playwright/test';
import { app } from '../test-utils';
test.describe('Registration', () => {
test.beforeEach(async () => {
test.beforeAll(async () => {
await app.reset();
});
+7 -16
View File
@@ -63,26 +63,17 @@ export const app = {
return response;
},
reset: async () => {
try {
if (!connected) {
await client.connect();
connected = true;
}
if (!connected) {
await client.connect();
}
for (const table of ['user_token', 'users', 'system_metadata']) {
await client.query(`DELETE FROM ${table} CASCADE;`);
}
} catch (error) {
console.error('Failed to reset database', error);
for (const table of ['users', 'system_metadata']) {
await client.query(`DELETE FROM ${table} CASCADE;`);
}
},
teardown: async () => {
try {
if (connected) {
await client.end();
}
} catch (error) {
console.error('Failed to teardown database', error);
if (connected) {
await client.end();
}
},
};
+1 -1
View File
@@ -59,7 +59,7 @@ start_docker_compose() {
}
show_friendly_message() {
echo "Successfully deployed Immich!"
echo "Succesfully deployed Immich!"
echo "You can access the website at http://$ip_address:2283 and the server URL for the mobile app is http://$ip_address:2283/api"
echo "The library location is $upload_location"
echo "---------------------------------------------------"
+3 -3
View File
@@ -33,10 +33,10 @@ linter:
# https://dart.dev/guides/language/analysis-options
analyzer:
exclude:
- openapi/**
- openapi/test/**
- openapi/
- openapi/test/
- lib/generated_plugin_registrant.dart
plugins:
- custom_lint
@@ -12,85 +12,92 @@ class AdvancedBottomSheet extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
return SingleChildScrollView(
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 8.0),
child: LayoutBuilder(
builder: (context, constraints) {
// One column
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Align(
child: Text(
"ADVANCED INFO",
style: TextStyle(fontSize: 12.0),
),
),
const SizedBox(height: 32.0),
Container(
decoration: BoxDecoration(
color: context.isDarkTheme
? Colors.grey[900]
: Colors.grey[200],
borderRadius: BorderRadius.circular(15.0),
),
child: Padding(
padding: const EdgeInsets.only(
right: 16.0,
left: 16,
top: 8,
bottom: 16,
child: Card(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15),
),
),
margin: const EdgeInsets.all(0),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 8.0),
child: LayoutBuilder(
builder: (context, constraints) {
// One column
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(height: 32.0),
const Align(
child: Text(
"ADVANCED INFO",
style: TextStyle(fontSize: 12.0),
),
child: ListView(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
children: [
Align(
alignment: Alignment.centerRight,
child: IconButton(
onPressed: () {
Clipboard.setData(
ClipboardData(
text: assetDetail.toString(),
),
).then((_) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Copied to clipboard",
style:
context.textTheme.bodyLarge?.copyWith(
color: context.primaryColor,
),
const SizedBox(height: 32.0),
Container(
decoration: BoxDecoration(
color: context.isDarkTheme
? Colors.grey[900]
: Colors.grey[200],
borderRadius: BorderRadius.circular(15.0),
),
child: Padding(
padding: const EdgeInsets.only(
right: 16.0,
left: 16,
top: 8,
bottom: 16,
),
child: ListView(
shrinkWrap: true,
children: [
Align(
alignment: Alignment.centerRight,
child: IconButton(
onPressed: () {
Clipboard.setData(
ClipboardData(text: assetDetail.toString()),
).then((_) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Copied to clipboard",
style: context.textTheme.bodyLarge
?.copyWith(
color: context.primaryColor,
),
),
),
),
);
});
},
icon: Icon(
Icons.copy,
size: 16.0,
color: context.primaryColor,
);
});
},
icon: Icon(
Icons.copy,
size: 16.0,
color: context.primaryColor,
),
),
),
),
SelectableText(
assetDetail.toString(),
style: const TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.bold,
fontFamily: "Inconsolata",
SelectableText(
assetDetail.toString(),
style: const TextStyle(
fontSize: 12.0,
fontWeight: FontWeight.bold,
fontFamily: "Inconsolata",
),
showCursor: true,
),
showCursor: true,
),
],
],
),
),
),
),
const SizedBox(height: 32.0),
],
);
},
const SizedBox(height: 32.0),
],
);
},
),
),
),
);
@@ -10,6 +10,7 @@ import 'package:immich_mobile/modules/asset_viewer/ui/description_input.dart';
import 'package:immich_mobile/modules/map/widgets/map_thumbnail.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/providers/asset.provider.dart';
import 'package:immich_mobile/shared/ui/drag_sheet.dart';
import 'package:immich_mobile/utils/selection_handlers.dart';
import 'package:immich_mobile/utils/bytes_units.dart';
import 'package:maplibre_gl/maplibre_gl.dart';
@@ -125,6 +126,20 @@ class ExifBottomSheet extends HookConsumerWidget {
return text.isNotEmpty ? text : null;
}
buildDragHeader() {
return const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(height: 12),
Align(
alignment: Alignment.center,
child: CustomDraggingHandle(),
),
SizedBox(height: 12),
],
);
}
buildLocation() {
// Guard no lat/lng
if (!hasCoordinates()) {
@@ -326,69 +341,86 @@ class ExifBottomSheet extends HookConsumerWidget {
);
}
return SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 16.0),
child: LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 600) {
// Two column
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
buildDate(),
if (asset.isRemote) DescriptionInput(asset: asset),
Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: buildLocation(),
return GestureDetector(
onTap: () {
// FocusScope.of(context).unfocus();
},
child: SingleChildScrollView(
child: Card(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(15),
topRight: Radius.circular(15),
),
),
margin: const EdgeInsets.all(0),
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 16.0),
child: LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 600) {
// Two column
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
buildDragHeader(),
buildDate(),
if (asset.isRemote) DescriptionInput(asset: asset),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Flexible(
flex: hasCoordinates() ? 5 : 0,
child: Padding(
padding: const EdgeInsets.only(right: 8.0),
child: buildLocation(),
),
),
Flexible(
flex: 5,
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: buildDetail(),
),
),
],
),
),
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 300),
child: Padding(
padding: const EdgeInsets.only(left: 8.0),
child: buildDetail(),
),
),
],
),
const SizedBox(height: 50),
],
);
}
const SizedBox(height: 50),
],
),
);
}
// One column
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
buildDate(),
assetWithExif.when(
data: (data) => DescriptionInput(asset: data),
error: (error, stackTrace) => Icon(
Icons.image_not_supported_outlined,
color: context.primaryColor,
),
loading: () => const SizedBox(
width: 75,
height: 75,
child: CircularProgressIndicator.adaptive(),
),
),
buildLocation(),
SizedBox(height: hasCoordinates() ? 16.0 : 6.0),
buildDetail(),
const SizedBox(height: 50),
],
);
},
// One column
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
buildDragHeader(),
buildDate(),
assetWithExif.when(
data: (data) => DescriptionInput(asset: data),
error: (error, stackTrace) => Icon(
Icons.image_not_supported_outlined,
color: context.primaryColor,
),
loading: () => const SizedBox(
width: 75,
height: 75,
child: CircularProgressIndicator.adaptive(),
),
),
buildLocation(),
SizedBox(height: hasCoordinates() ? 16.0 : 6.0),
buildDetail(),
const SizedBox(height: 50),
],
);
},
),
),
),
),
);
@@ -136,7 +136,6 @@ class GalleryViewerPage extends HookConsumerWidget {
// swallow error silently
debugPrint('Error precaching next image: $exception, $stackTrace');
}
if (index < totalAssets && index >= 0) {
final asset = loadAsset(index);
precacheImage(
@@ -153,11 +152,10 @@ class GalleryViewerPage extends HookConsumerWidget {
borderRadius: BorderRadius.all(Radius.circular(15.0)),
),
barrierColor: Colors.transparent,
backgroundColor: Colors.transparent,
isScrollControlled: true,
showDragHandle: true,
enableDrag: true,
context: context,
useSafeArea: true,
context: context,
builder: (context) {
return Padding(
padding: EdgeInsets.only(
@@ -88,7 +88,7 @@ class UploadProfileImageNotifier
var res = await _userSErvice.uploadProfileImage(file);
if (res != null) {
debugPrint("Successfully upload profile image");
debugPrint("Succesfully upload profile image");
state = state.copyWith(
status: UploadProfileStatus.success,
profileImagePath: res.profileImagePath,
@@ -10,7 +10,6 @@ import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/extensions/collection_extensions.dart';
import 'package:immich_mobile/modules/asset_viewer/providers/scroll_notifier.provider.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_image.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_placeholder.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
@@ -128,6 +127,184 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
assets.firstWhereOrNull((e) => !_selectedAssets.contains(e)) == null;
}
Widget _buildThumbnailOrPlaceholder(Asset asset, int index) {
return ThumbnailImage(
asset: asset,
index: index,
loadAsset: widget.renderList.loadAsset,
totalAssets: widget.renderList.totalAssets,
multiselectEnabled: widget.selectionActive,
isSelected: widget.selectionActive && _selectedAssets.contains(asset),
onSelect: () => _selectAssets([asset]),
onDeselect: widget.canDeselect ||
widget.preselectedAssets == null ||
!widget.preselectedAssets!.contains(asset)
? () => _deselectAssets([asset])
: null,
useGrayBoxPlaceholder: true,
showStorageIndicator: widget.showStorageIndicator,
heroOffset: widget.heroOffset,
showStack: widget.showStack,
);
}
Widget _buildAssetRow(
Key key,
BuildContext context,
List<Asset> assets,
int absoluteOffset,
double width,
) {
// Default: All assets have the same width
final widthDistribution = List.filled(assets.length, 1.0);
if (widget.dynamicLayout) {
final aspectRatios =
assets.map((e) => (e.width ?? 1) / (e.height ?? 1)).toList();
final meanAspectRatio = aspectRatios.sum / assets.length;
// 1: mean width
// 0.5: width < mean - threshold
// 1.5: width > mean + threshold
final arConfiguration = aspectRatios.map((e) {
if (e - meanAspectRatio > 0.3) return 1.5;
if (e - meanAspectRatio < -0.3) return 0.5;
return 1.0;
});
// Normalize:
final sum = arConfiguration.sum;
widthDistribution.setRange(
0,
widthDistribution.length,
arConfiguration.map((e) => (e * assets.length) / sum),
);
}
return Row(
key: key,
children: assets.mapIndexed((int index, Asset asset) {
final bool last = index + 1 == widget.assetsPerRow;
return Container(
key: ValueKey(index),
width: width * widthDistribution[index],
height: width,
margin: EdgeInsets.only(
bottom: widget.margin,
right: last ? 0.0 : widget.margin,
),
child: _buildThumbnailOrPlaceholder(asset, absoluteOffset + index),
);
}).toList(),
);
}
Widget _buildTitle(
BuildContext context,
String title,
List<Asset> assets,
) {
return GroupDividerTitle(
text: title,
multiselectEnabled: widget.selectionActive,
onSelect: () => _selectAssets(assets),
onDeselect: () => _deselectAssets(assets),
selected: _allAssetsSelected(assets),
);
}
Widget _buildMonthTitle(BuildContext context, DateTime date) {
final monthFormat = DateTime.now().year == date.year
? DateFormat.MMMM()
: DateFormat.yMMMM();
final String title = monthFormat.format(date);
return Padding(
key: Key("month-$title"),
padding: const EdgeInsets.only(left: 12.0, top: 24.0),
child: Text(
title,
style: const TextStyle(
fontSize: 26,
fontWeight: FontWeight.w500,
),
),
);
}
Widget _buildPlaceHolderRow(Key key, int num, double width, double height) {
return Row(
key: key,
children: [
for (int i = 0; i < num; i++)
Container(
key: ValueKey(i),
width: width,
height: height,
margin: EdgeInsets.only(
bottom: widget.margin,
right: i + 1 == num ? 0.0 : widget.margin,
),
color: Colors.grey,
),
],
);
}
Widget _buildSection(
BuildContext context,
RenderAssetGridElement section,
bool scrolling,
) {
return LayoutBuilder(
builder: (context, constraints) {
final width = constraints.maxWidth / widget.assetsPerRow -
widget.margin * (widget.assetsPerRow - 1) / widget.assetsPerRow;
final rows =
(section.count + widget.assetsPerRow - 1) ~/ widget.assetsPerRow;
final List<Asset> assetsToRender = scrolling
? []
: widget.renderList.loadAssets(section.offset, section.count);
return Column(
key: ValueKey(section.offset),
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (section.type == RenderAssetGridElementType.monthTitle)
_buildMonthTitle(context, section.date),
if (section.type == RenderAssetGridElementType.groupDividerTitle ||
section.type == RenderAssetGridElementType.monthTitle)
_buildTitle(
context,
section.title!,
scrolling
? []
: widget.renderList
.loadAssets(section.offset, section.totalCount),
),
for (int i = 0; i < rows; i++)
scrolling
? _buildPlaceHolderRow(
ValueKey(i),
i + 1 == rows
? section.count - i * widget.assetsPerRow
: widget.assetsPerRow,
width,
width,
)
: _buildAssetRow(
ValueKey(i),
context,
assetsToRender.nestedSlice(
i * widget.assetsPerRow,
min((i + 1) * widget.assetsPerRow, section.count),
),
section.offset + i * widget.assetsPerRow,
width,
),
],
);
},
);
}
Widget _itemBuilder(BuildContext c, int position) {
int index = position;
if (widget.topWidget != null) {
@@ -137,23 +314,8 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
index--;
}
final section = widget.renderList.elements[index];
return _Section(
showStorageIndicator: widget.showStorageIndicator,
selectedAssets: _selectedAssets,
selectionActive: widget.selectionActive,
section: section,
margin: widget.margin,
renderList: widget.renderList,
assetsPerRow: widget.assetsPerRow,
scrolling: _scrolling,
dynamicLayout: widget.dynamicLayout,
selectAssets: _selectAssets,
deselectAssets: _deselectAssets,
allAssetsSelected: _allAssetsSelected,
showStack: widget.showStack,
heroOffset: widget.heroOffset,
);
final item = widget.renderList.elements[index];
return _buildSection(c, item, _scrolling);
}
Text _labelBuilder(int pos) {
@@ -323,292 +485,3 @@ class ImmichAssetGridViewState extends State<ImmichAssetGridView> {
);
}
}
/// A single row of all placeholder widgets
class _PlaceholderRow extends StatelessWidget {
final int number;
final double width;
final double height;
final double margin;
const _PlaceholderRow({
super.key,
required this.number,
required this.width,
required this.height,
required this.margin,
});
@override
Widget build(BuildContext context) {
return Row(
children: [
for (int i = 0; i < number; i++)
ThumbnailPlaceholder(
key: ValueKey(i),
width: width,
height: height,
margin: EdgeInsets.only(
bottom: margin,
right: i + 1 == number ? 0.0 : margin,
),
),
],
);
}
}
/// A section for the render grid
class _Section extends StatelessWidget {
final RenderAssetGridElement section;
final Set<Asset> selectedAssets;
final bool scrolling;
final double margin;
final int assetsPerRow;
final RenderList renderList;
final bool selectionActive;
final bool dynamicLayout;
final Function(List<Asset>) selectAssets;
final Function(List<Asset>) deselectAssets;
final bool Function(List<Asset>) allAssetsSelected;
final bool showStack;
final int heroOffset;
final bool showStorageIndicator;
const _Section({
required this.section,
required this.scrolling,
required this.margin,
required this.assetsPerRow,
required this.renderList,
required this.selectionActive,
required this.dynamicLayout,
required this.selectAssets,
required this.deselectAssets,
required this.allAssetsSelected,
required this.selectedAssets,
required this.showStack,
required this.heroOffset,
required this.showStorageIndicator,
});
@override
Widget build(
BuildContext context,
) {
return LayoutBuilder(
builder: (context, constraints) {
final width = constraints.maxWidth / assetsPerRow -
margin * (assetsPerRow - 1) / assetsPerRow;
final rows = (section.count + assetsPerRow - 1) ~/ assetsPerRow;
final List<Asset> assetsToRender = scrolling
? []
: renderList.loadAssets(section.offset, section.count);
return Column(
key: ValueKey(section.offset),
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (section.type == RenderAssetGridElementType.monthTitle)
_MonthTitle(date: section.date),
if (section.type == RenderAssetGridElementType.groupDividerTitle ||
section.type == RenderAssetGridElementType.monthTitle)
_Title(
selectionActive: selectionActive,
title: section.title!,
assets: scrolling
? []
: renderList.loadAssets(section.offset, section.totalCount),
allAssetsSelected: allAssetsSelected,
selectAssets: selectAssets,
deselectAssets: deselectAssets,
),
for (int i = 0; i < rows; i++)
scrolling
? _PlaceholderRow(
key: ValueKey(i),
number: i + 1 == rows
? section.count - i * assetsPerRow
: assetsPerRow,
width: width,
height: width,
margin: margin,
)
: _AssetRow(
key: ValueKey(i),
assets: assetsToRender.nestedSlice(
i * assetsPerRow,
min((i + 1) * assetsPerRow, section.count),
),
absoluteOffset: section.offset + i * assetsPerRow,
width: width,
assetsPerRow: assetsPerRow,
margin: margin,
dynamicLayout: dynamicLayout,
renderList: renderList,
selectedAssets: selectedAssets,
isSelectionActive: selectionActive,
showStack: showStack,
heroOffset: heroOffset,
showStorageIndicator: showStorageIndicator,
selectionActive: selectionActive,
onSelect: (asset) => selectAssets([asset]),
onDeselect: (asset) => deselectAssets([asset]),
),
],
);
},
);
}
}
/// The month title row for a section
class _MonthTitle extends StatelessWidget {
final DateTime date;
const _MonthTitle({
required this.date,
});
@override
Widget build(BuildContext context) {
final monthFormat = DateTime.now().year == date.year
? DateFormat.MMMM()
: DateFormat.yMMMM();
final String title = monthFormat.format(date);
return Padding(
key: Key("month-$title"),
padding: const EdgeInsets.only(left: 12.0, top: 24.0),
child: Text(
title,
style: const TextStyle(
fontSize: 26,
fontWeight: FontWeight.w500,
),
),
);
}
}
/// A title row
class _Title extends StatelessWidget {
final String title;
final List<Asset> assets;
final bool selectionActive;
final Function(List<Asset>) selectAssets;
final Function(List<Asset>) deselectAssets;
final Function(List<Asset>) allAssetsSelected;
const _Title({
required this.title,
required this.assets,
required this.selectionActive,
required this.selectAssets,
required this.deselectAssets,
required this.allAssetsSelected,
});
@override
Widget build(BuildContext context) {
return GroupDividerTitle(
text: title,
multiselectEnabled: selectionActive,
onSelect: () => selectAssets(assets),
onDeselect: () => deselectAssets(assets),
selected: allAssetsSelected(assets),
);
}
}
/// The row of assets
class _AssetRow extends StatelessWidget {
final List<Asset> assets;
final Set<Asset> selectedAssets;
final int absoluteOffset;
final double width;
final bool dynamicLayout;
final double margin;
final int assetsPerRow;
final RenderList renderList;
final bool selectionActive;
final bool showStorageIndicator;
final int heroOffset;
final bool showStack;
final Function(Asset)? onSelect;
final Function(Asset)? onDeselect;
final bool isSelectionActive;
const _AssetRow({
super.key,
required this.assets,
required this.absoluteOffset,
required this.width,
required this.dynamicLayout,
required this.margin,
required this.assetsPerRow,
required this.renderList,
required this.selectionActive,
required this.showStorageIndicator,
required this.heroOffset,
required this.showStack,
required this.isSelectionActive,
required this.selectedAssets,
this.onSelect,
this.onDeselect,
});
@override
Widget build(BuildContext context) {
// Default: All assets have the same width
final widthDistribution = List.filled(assets.length, 1.0);
if (dynamicLayout) {
final aspectRatios =
assets.map((e) => (e.width ?? 1) / (e.height ?? 1)).toList();
final meanAspectRatio = aspectRatios.sum / assets.length;
// 1: mean width
// 0.5: width < mean - threshold
// 1.5: width > mean + threshold
final arConfiguration = aspectRatios.map((e) {
if (e - meanAspectRatio > 0.3) return 1.5;
if (e - meanAspectRatio < -0.3) return 0.5;
return 1.0;
});
// Normalize:
final sum = arConfiguration.sum;
widthDistribution.setRange(
0,
widthDistribution.length,
arConfiguration.map((e) => (e * assets.length) / sum),
);
}
return Row(
key: key,
children: assets.mapIndexed((int index, Asset asset) {
final bool last = index + 1 == assetsPerRow;
return Container(
width: width * widthDistribution[index],
height: width,
margin: EdgeInsets.only(
bottom: margin,
right: last ? 0.0 : margin,
),
child: ThumbnailImage(
asset: asset,
index: absoluteOffset + index,
loadAsset: renderList.loadAsset,
totalAssets: renderList.totalAssets,
multiselectEnabled: selectionActive,
isSelected: isSelectionActive && selectedAssets.contains(asset),
onSelect: () => onSelect?.call(asset),
onDeselect: () => onDeselect?.call(asset),
showStorageIndicator: showStorageIndicator,
heroOffset: heroOffset,
showStack: showStack,
),
);
}).toList(),
);
}
}
@@ -15,6 +15,7 @@ class ThumbnailImage extends StatelessWidget {
final int totalAssets;
final bool showStorageIndicator;
final bool showStack;
final bool useGrayBoxPlaceholder;
final bool isSelected;
final bool multiselectEnabled;
final Function? onSelect;
@@ -29,6 +30,7 @@ class ThumbnailImage extends StatelessWidget {
required this.totalAssets,
this.showStorageIndicator = true,
this.showStack = false,
this.useGrayBoxPlaceholder = false,
this.isSelected = false,
this.multiselectEnabled = false,
this.onDeselect,
@@ -136,8 +138,6 @@ class ThumbnailImage extends StatelessWidget {
: asset.id + heroOffset,
child: ImmichImage.thumbnail(
asset,
height: 300,
width: 300,
),
),
);
@@ -1,41 +0,0 @@
import 'package:flutter/material.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
class ThumbnailPlaceholder extends StatelessWidget {
final EdgeInsets margin;
final double width;
final double height;
const ThumbnailPlaceholder({
super.key,
this.margin = EdgeInsets.zero,
this.width = 250,
this.height = 250,
});
static const _brightColors = [
Color(0xFFF1F3F4),
Color(0xFFB4B6B8),
];
static const _darkColors = [
Color(0xFF3B3F42),
Color(0xFF2B2F32),
];
@override
Widget build(BuildContext context) {
return Container(
width: width,
height: height,
margin: margin,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: context.isDarkTheme ? _darkColors : _brightColors,
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
),
),
);
}
}
@@ -125,19 +125,6 @@ class ControlBottomAppBar extends ConsumerWidget {
.tr(),
onPressed: enabled ? onFavorite : null,
),
if (hasLocal && hasRemote && onDelete != null)
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 90),
child: ControlBoxButton(
iconData: Icons.delete_sweep_outlined,
label: "control_bottom_app_bar_delete".tr(),
onPressed: enabled
? () => handleRemoteDelete(!trashEnabled, onDelete!)
: null,
onLongPressed:
enabled ? () => showForceDeleteDialog(onDelete!) : null,
),
),
if (hasRemote && onDeleteServer != null)
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 85),
@@ -185,43 +172,44 @@ class ControlBottomAppBar extends ConsumerWidget {
: null,
),
),
if (hasRemote && onEditTime != null)
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 95),
child: ControlBoxButton(
iconData: Icons.edit_calendar_outlined,
label: "control_bottom_app_bar_edit_time".tr(),
onPressed: enabled ? onEditTime : null,
),
),
if (hasRemote && onEditLocation != null)
if (hasLocal && hasRemote && onDelete != null)
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 90),
child: ControlBoxButton(
iconData: Icons.edit_location_alt_outlined,
label: "control_bottom_app_bar_edit_location".tr(),
onPressed: enabled ? onEditLocation : null,
iconData: Icons.delete_sweep_outlined,
label: "control_bottom_app_bar_delete".tr(),
onPressed: enabled
? () => handleRemoteDelete(!trashEnabled, onDelete!)
: null,
onLongPressed:
enabled ? () => showForceDeleteDialog(onDelete!) : null,
),
),
if (hasRemote && onEditTime != null)
ControlBoxButton(
iconData: Icons.edit_calendar_outlined,
label: "control_bottom_app_bar_edit_time".tr(),
onPressed: enabled ? onEditTime : null,
),
if (hasRemote && onEditLocation != null)
ControlBoxButton(
iconData: Icons.edit_location_alt_outlined,
label: "control_bottom_app_bar_edit_location".tr(),
onPressed: enabled ? onEditLocation : null,
),
if (!selectionAssetState.hasLocal &&
selectionAssetState.selectedCount > 1 &&
onStack != null)
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 90),
child: ControlBoxButton(
iconData: Icons.filter_none_rounded,
label: "control_bottom_app_bar_stack".tr(),
onPressed: enabled ? onStack : null,
),
ControlBoxButton(
iconData: Icons.filter_none_rounded,
label: "control_bottom_app_bar_stack".tr(),
onPressed: enabled ? onStack : null,
),
if (onRemoveFromAlbum != null)
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 90),
child: ControlBoxButton(
iconData: Icons.delete_sweep_rounded,
label: 'album_viewer_appbar_share_remove'.tr(),
onPressed: enabled ? onRemoveFromAlbum : null,
),
ControlBoxButton(
iconData: Icons.delete_sweep_rounded,
label: 'album_viewer_appbar_share_remove'.tr(),
onPressed: enabled ? onRemoveFromAlbum : null,
),
if (selectionAssetState.hasLocal)
ControlBoxButton(
@@ -242,9 +230,9 @@ class ControlBottomAppBar extends ConsumerWidget {
}
return DraggableScrollableSheet(
initialChildSize: hasRemote ? 0.35 : bottomPadding,
initialChildSize: hasRemote ? 0.30 : bottomPadding,
minChildSize: bottomPadding,
maxChildSize: hasRemote ? 0.65 : bottomPadding,
maxChildSize: hasRemote ? 0.60 : bottomPadding,
snap: true,
builder: (
BuildContext context,
@@ -269,9 +257,9 @@ class ControlBottomAppBar extends ConsumerWidget {
children: <Widget>[
const SizedBox(height: 12),
const CustomDraggingHandle(),
const SizedBox(height: 12),
const SizedBox(height: 24),
SizedBox(
height: 100,
height: 90,
child: ListView(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
@@ -2,7 +2,6 @@ import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_placeholder.dart';
import 'package:immich_mobile/modules/memories/providers/memory.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/shared/ui/immich_image.dart';
@@ -61,10 +60,7 @@ class MemoryLane extends HookConsumerWidget {
fit: BoxFit.cover,
width: 130,
height: 200,
placeholder: const ThumbnailPlaceholder(
width: 130,
height: 200,
),
useGrayBoxPlaceholder: true,
),
),
),
+10 -9
View File
@@ -5,7 +5,6 @@ import 'package:flutter/services.dart';
import 'package:immich_mobile/extensions/build_context_extensions.dart';
import 'package:immich_mobile/modules/asset_viewer/image_providers/immich_local_image_provider.dart';
import 'package:immich_mobile/modules/asset_viewer/image_providers/immich_remote_image_provider.dart';
import 'package:immich_mobile/modules/home/ui/asset_grid/thumbnail_placeholder.dart';
import 'package:immich_mobile/shared/models/asset.dart';
import 'package:immich_mobile/shared/models/store.dart';
import 'package:octo_image/octo_image.dart';
@@ -18,14 +17,14 @@ class ImmichImage extends StatelessWidget {
this.width,
this.height,
this.fit = BoxFit.cover,
this.placeholder = const ThumbnailPlaceholder(),
this.useGrayBoxPlaceholder = false,
this.isThumbnail = false,
this.thumbnailSize = 250,
super.key,
});
final Asset? asset;
final Widget? placeholder;
final bool useGrayBoxPlaceholder;
final double? width;
final double? height;
final BoxFit fit;
@@ -48,10 +47,7 @@ class ImmichImage extends StatelessWidget {
fit: fit,
width: width,
height: height,
placeholder: ThumbnailPlaceholder(
height: thumbnailSize.toDouble(),
width: thumbnailSize.toDouble(),
),
useGrayBoxPlaceholder: true,
thumbnailSize: thumbnailSize,
);
}
@@ -103,6 +99,7 @@ class ImmichImage extends StatelessWidget {
asset.isLocal && !Store.get(StoreKey.preferRemoteImage, false);
@override
Widget build(BuildContext context) {
if (asset == null) {
return Container(
decoration: const BoxDecoration(
@@ -122,9 +119,13 @@ class ImmichImage extends StatelessWidget {
fadeInDuration: const Duration(milliseconds: 0),
fadeOutDuration: const Duration(milliseconds: 400),
placeholderBuilder: (context) {
if (placeholder != null) {
if (useGrayBoxPlaceholder) {
// Use the gray box placeholder
return placeholder!;
return const SizedBox.expand(
child: DecoratedBox(
decoration: BoxDecoration(color: Colors.grey),
),
);
}
// No placeholder
return const SizedBox();
-6
View File
@@ -90,7 +90,6 @@ doc/MapMarkerResponseDto.md
doc/MapTheme.md
doc/MemoryLaneResponseDto.md
doc/MergePersonDto.md
doc/MetadataSearchDto.md
doc/ModelType.md
doc/OAuthApi.md
doc/OAuthAuthorizeResponseDto.md
@@ -138,7 +137,6 @@ doc/SharedLinkResponseDto.md
doc/SharedLinkType.md
doc/SignUpDto.md
doc/SmartInfoResponseDto.md
doc/SmartSearchDto.md
doc/SystemConfigApi.md
doc/SystemConfigDto.md
doc/SystemConfigFFmpegDto.md
@@ -290,7 +288,6 @@ lib/model/map_marker_response_dto.dart
lib/model/map_theme.dart
lib/model/memory_lane_response_dto.dart
lib/model/merge_person_dto.dart
lib/model/metadata_search_dto.dart
lib/model/model_type.dart
lib/model/o_auth_authorize_response_dto.dart
lib/model/o_auth_callback_dto.dart
@@ -332,7 +329,6 @@ lib/model/shared_link_response_dto.dart
lib/model/shared_link_type.dart
lib/model/sign_up_dto.dart
lib/model/smart_info_response_dto.dart
lib/model/smart_search_dto.dart
lib/model/system_config_dto.dart
lib/model/system_config_f_fmpeg_dto.dart
lib/model/system_config_job_dto.dart
@@ -461,7 +457,6 @@ test/map_marker_response_dto_test.dart
test/map_theme_test.dart
test/memory_lane_response_dto_test.dart
test/merge_person_dto_test.dart
test/metadata_search_dto_test.dart
test/model_type_test.dart
test/o_auth_api_test.dart
test/o_auth_authorize_response_dto_test.dart
@@ -509,7 +504,6 @@ test/shared_link_response_dto_test.dart
test/shared_link_type_test.dart
test/sign_up_dto_test.dart
test/smart_info_response_dto_test.dart
test/smart_search_dto_test.dart
test/system_config_api_test.dart
test/system_config_dto_test.dart
test/system_config_f_fmpeg_dto_test.dart
+2 -4
View File
@@ -163,9 +163,9 @@ Class | Method | HTTP request | Description
*SearchApi* | [**getExploreData**](doc//SearchApi.md#getexploredata) | **GET** /search/explore |
*SearchApi* | [**getSearchSuggestions**](doc//SearchApi.md#getsearchsuggestions) | **GET** /search/suggestions |
*SearchApi* | [**search**](doc//SearchApi.md#search) | **GET** /search |
*SearchApi* | [**searchMetadata**](doc//SearchApi.md#searchmetadata) | **POST** /search/metadata |
*SearchApi* | [**searchMetadata**](doc//SearchApi.md#searchmetadata) | **GET** /search/metadata |
*SearchApi* | [**searchPerson**](doc//SearchApi.md#searchperson) | **GET** /search/person |
*SearchApi* | [**searchSmart**](doc//SearchApi.md#searchsmart) | **POST** /search/smart |
*SearchApi* | [**searchSmart**](doc//SearchApi.md#searchsmart) | **GET** /search/smart |
*ServerInfoApi* | [**getServerConfig**](doc//ServerInfoApi.md#getserverconfig) | **GET** /server-info/config |
*ServerInfoApi* | [**getServerFeatures**](doc//ServerInfoApi.md#getserverfeatures) | **GET** /server-info/features |
*ServerInfoApi* | [**getServerInfo**](doc//ServerInfoApi.md#getserverinfo) | **GET** /server-info |
@@ -290,7 +290,6 @@ Class | Method | HTTP request | Description
- [MapTheme](doc//MapTheme.md)
- [MemoryLaneResponseDto](doc//MemoryLaneResponseDto.md)
- [MergePersonDto](doc//MergePersonDto.md)
- [MetadataSearchDto](doc//MetadataSearchDto.md)
- [ModelType](doc//ModelType.md)
- [OAuthAuthorizeResponseDto](doc//OAuthAuthorizeResponseDto.md)
- [OAuthCallbackDto](doc//OAuthCallbackDto.md)
@@ -332,7 +331,6 @@ Class | Method | HTTP request | Description
- [SharedLinkType](doc//SharedLinkType.md)
- [SignUpDto](doc//SignUpDto.md)
- [SmartInfoResponseDto](doc//SmartInfoResponseDto.md)
- [SmartSearchDto](doc//SmartSearchDto.md)
- [SystemConfigDto](doc//SystemConfigDto.md)
- [SystemConfigFFmpegDto](doc//SystemConfigFFmpegDto.md)
- [SystemConfigJobDto](doc//SystemConfigJobDto.md)
+4 -10
View File
@@ -659,7 +659,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)
# **getMapMarkers**
> List<MapMarkerResponseDto> getMapMarkers(fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite, withPartners)
> List<MapMarkerResponseDto> getMapMarkers(fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite)
@@ -686,10 +686,9 @@ final fileCreatedAfter = 2013-10-20T19:20:30+01:00; // DateTime |
final fileCreatedBefore = 2013-10-20T19:20:30+01:00; // DateTime |
final isArchived = true; // bool |
final isFavorite = true; // bool |
final withPartners = true; // bool |
try {
final result = api_instance.getMapMarkers(fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite, withPartners);
final result = api_instance.getMapMarkers(fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite);
print(result);
} catch (e) {
print('Exception when calling AssetApi->getMapMarkers: $e\n');
@@ -704,7 +703,6 @@ Name | Type | Description | Notes
**fileCreatedBefore** | **DateTime**| | [optional]
**isArchived** | **bool**| | [optional]
**isFavorite** | **bool**| | [optional]
**withPartners** | **bool**| | [optional]
### Return type
@@ -1036,7 +1034,7 @@ void (empty response body)
[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)
# **searchAssets**
> List<AssetResponseDto> searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked)
> List<AssetResponseDto> searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked)
@@ -1073,7 +1071,6 @@ final isEncoded = true; // bool |
final isExternal = true; // bool |
final isFavorite = true; // bool |
final isMotion = true; // bool |
final isNotInAlbum = true; // bool |
final isOffline = true; // bool |
final isReadOnly = true; // bool |
final isVisible = true; // bool |
@@ -1085,7 +1082,6 @@ final order = ; // AssetOrder |
final originalFileName = originalFileName_example; // String |
final originalPath = originalPath_example; // String |
final page = 8.14; // num |
final personIds = []; // List<String> |
final resizePath = resizePath_example; // String |
final size = 8.14; // num |
final state = state_example; // String |
@@ -1104,7 +1100,7 @@ final withPeople = true; // bool |
final withStacked = true; // bool |
try {
final result = api_instance.searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked);
final result = api_instance.searchAssets(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked);
print(result);
} catch (e) {
print('Exception when calling AssetApi->searchAssets: $e\n');
@@ -1129,7 +1125,6 @@ Name | Type | Description | Notes
**isExternal** | **bool**| | [optional]
**isFavorite** | **bool**| | [optional]
**isMotion** | **bool**| | [optional]
**isNotInAlbum** | **bool**| | [optional]
**isOffline** | **bool**| | [optional]
**isReadOnly** | **bool**| | [optional]
**isVisible** | **bool**| | [optional]
@@ -1141,7 +1136,6 @@ Name | Type | Description | Notes
**originalFileName** | **String**| | [optional]
**originalPath** | **String**| | [optional]
**page** | **num**| | [optional]
**personIds** | [**List<String>**](String.md)| | [optional] [default to const []]
**resizePath** | **String**| | [optional]
**size** | **num**| | [optional]
**state** | **String**| | [optional]
-57
View File
@@ -1,57 +0,0 @@
# openapi.model.MetadataSearchDto
## Load the model package
```dart
import 'package:openapi/api.dart';
```
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**checksum** | **String** | | [optional]
**city** | **String** | | [optional]
**country** | **String** | | [optional]
**createdAfter** | [**DateTime**](DateTime.md) | | [optional]
**createdBefore** | [**DateTime**](DateTime.md) | | [optional]
**deviceAssetId** | **String** | | [optional]
**deviceId** | **String** | | [optional]
**encodedVideoPath** | **String** | | [optional]
**id** | **String** | | [optional]
**isArchived** | **bool** | | [optional]
**isEncoded** | **bool** | | [optional]
**isExternal** | **bool** | | [optional]
**isFavorite** | **bool** | | [optional]
**isMotion** | **bool** | | [optional]
**isNotInAlbum** | **bool** | | [optional]
**isOffline** | **bool** | | [optional]
**isReadOnly** | **bool** | | [optional]
**isVisible** | **bool** | | [optional]
**lensModel** | **String** | | [optional]
**libraryId** | **String** | | [optional]
**make** | **String** | | [optional]
**model** | **String** | | [optional]
**order** | [**AssetOrder**](AssetOrder.md) | | [optional]
**originalFileName** | **String** | | [optional]
**originalPath** | **String** | | [optional]
**page** | **num** | | [optional]
**personIds** | **List<String>** | | [optional] [default to const []]
**resizePath** | **String** | | [optional]
**size** | **num** | | [optional]
**state** | **String** | | [optional]
**takenAfter** | [**DateTime**](DateTime.md) | | [optional]
**takenBefore** | [**DateTime**](DateTime.md) | | [optional]
**trashedAfter** | [**DateTime**](DateTime.md) | | [optional]
**trashedBefore** | [**DateTime**](DateTime.md) | | [optional]
**type** | [**AssetTypeEnum**](AssetTypeEnum.md) | | [optional]
**updatedAfter** | [**DateTime**](DateTime.md) | | [optional]
**updatedBefore** | [**DateTime**](DateTime.md) | | [optional]
**webpPath** | **String** | | [optional]
**withArchived** | **bool** | | [optional]
**withDeleted** | **bool** | | [optional]
**withExif** | **bool** | | [optional]
**withPeople** | **bool** | | [optional]
**withStacked** | **bool** | | [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)
+152 -12
View File
@@ -12,9 +12,9 @@ Method | HTTP request | Description
[**getExploreData**](SearchApi.md#getexploredata) | **GET** /search/explore |
[**getSearchSuggestions**](SearchApi.md#getsearchsuggestions) | **GET** /search/suggestions |
[**search**](SearchApi.md#search) | **GET** /search |
[**searchMetadata**](SearchApi.md#searchmetadata) | **POST** /search/metadata |
[**searchMetadata**](SearchApi.md#searchmetadata) | **GET** /search/metadata |
[**searchPerson**](SearchApi.md#searchperson) | **GET** /search/person |
[**searchSmart**](SearchApi.md#searchsmart) | **POST** /search/smart |
[**searchSmart**](SearchApi.md#searchsmart) | **GET** /search/smart |
# **getExploreData**
@@ -205,7 +205,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)
# **searchMetadata**
> SearchResponseDto searchMetadata(metadataSearchDto)
> SearchResponseDto searchMetadata(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked)
@@ -228,10 +228,50 @@ import 'package:openapi/api.dart';
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = SearchApi();
final metadataSearchDto = MetadataSearchDto(); // MetadataSearchDto |
final checksum = checksum_example; // String |
final city = city_example; // String |
final country = country_example; // String |
final createdAfter = 2013-10-20T19:20:30+01:00; // DateTime |
final createdBefore = 2013-10-20T19:20:30+01:00; // DateTime |
final deviceAssetId = deviceAssetId_example; // String |
final deviceId = deviceId_example; // String |
final encodedVideoPath = encodedVideoPath_example; // String |
final id = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
final isArchived = true; // bool |
final isEncoded = true; // bool |
final isExternal = true; // bool |
final isFavorite = true; // bool |
final isMotion = true; // bool |
final isOffline = true; // bool |
final isReadOnly = true; // bool |
final isVisible = true; // bool |
final lensModel = lensModel_example; // String |
final libraryId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
final make = make_example; // String |
final model = model_example; // String |
final order = ; // AssetOrder |
final originalFileName = originalFileName_example; // String |
final originalPath = originalPath_example; // String |
final page = 8.14; // num |
final resizePath = resizePath_example; // String |
final size = 8.14; // num |
final state = state_example; // String |
final takenAfter = 2013-10-20T19:20:30+01:00; // DateTime |
final takenBefore = 2013-10-20T19:20:30+01:00; // DateTime |
final trashedAfter = 2013-10-20T19:20:30+01:00; // DateTime |
final trashedBefore = 2013-10-20T19:20:30+01:00; // DateTime |
final type = ; // AssetTypeEnum |
final updatedAfter = 2013-10-20T19:20:30+01:00; // DateTime |
final updatedBefore = 2013-10-20T19:20:30+01:00; // DateTime |
final webpPath = webpPath_example; // String |
final withArchived = true; // bool |
final withDeleted = true; // bool |
final withExif = true; // bool |
final withPeople = true; // bool |
final withStacked = true; // bool |
try {
final result = api_instance.searchMetadata(metadataSearchDto);
final result = api_instance.searchMetadata(checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked);
print(result);
} catch (e) {
print('Exception when calling SearchApi->searchMetadata: $e\n');
@@ -242,7 +282,47 @@ try {
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**metadataSearchDto** | [**MetadataSearchDto**](MetadataSearchDto.md)| |
**checksum** | **String**| | [optional]
**city** | **String**| | [optional]
**country** | **String**| | [optional]
**createdAfter** | **DateTime**| | [optional]
**createdBefore** | **DateTime**| | [optional]
**deviceAssetId** | **String**| | [optional]
**deviceId** | **String**| | [optional]
**encodedVideoPath** | **String**| | [optional]
**id** | **String**| | [optional]
**isArchived** | **bool**| | [optional]
**isEncoded** | **bool**| | [optional]
**isExternal** | **bool**| | [optional]
**isFavorite** | **bool**| | [optional]
**isMotion** | **bool**| | [optional]
**isOffline** | **bool**| | [optional]
**isReadOnly** | **bool**| | [optional]
**isVisible** | **bool**| | [optional]
**lensModel** | **String**| | [optional]
**libraryId** | **String**| | [optional]
**make** | **String**| | [optional]
**model** | **String**| | [optional]
**order** | [**AssetOrder**](.md)| | [optional]
**originalFileName** | **String**| | [optional]
**originalPath** | **String**| | [optional]
**page** | **num**| | [optional]
**resizePath** | **String**| | [optional]
**size** | **num**| | [optional]
**state** | **String**| | [optional]
**takenAfter** | **DateTime**| | [optional]
**takenBefore** | **DateTime**| | [optional]
**trashedAfter** | **DateTime**| | [optional]
**trashedBefore** | **DateTime**| | [optional]
**type** | [**AssetTypeEnum**](.md)| | [optional]
**updatedAfter** | **DateTime**| | [optional]
**updatedBefore** | **DateTime**| | [optional]
**webpPath** | **String**| | [optional]
**withArchived** | **bool**| | [optional]
**withDeleted** | **bool**| | [optional]
**withExif** | **bool**| | [optional]
**withPeople** | **bool**| | [optional]
**withStacked** | **bool**| | [optional]
### Return type
@@ -254,7 +334,7 @@ Name | Type | Description | Notes
### HTTP request headers
- **Content-Type**: application/json
- **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)
@@ -317,7 +397,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)
# **searchSmart**
> SearchResponseDto searchSmart(smartSearchDto)
> SearchResponseDto searchSmart(query, city, country, createdAfter, createdBefore, deviceId, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, page, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, withArchived, withDeleted, withExif)
@@ -340,10 +420,40 @@ import 'package:openapi/api.dart';
//defaultApiClient.getAuthentication<HttpBearerAuth>('bearer').setAccessToken(yourTokenGeneratorFunction);
final api_instance = SearchApi();
final smartSearchDto = SmartSearchDto(); // SmartSearchDto |
final query = query_example; // String |
final city = city_example; // String |
final country = country_example; // String |
final createdAfter = 2013-10-20T19:20:30+01:00; // DateTime |
final createdBefore = 2013-10-20T19:20:30+01:00; // DateTime |
final deviceId = deviceId_example; // String |
final isArchived = true; // bool |
final isEncoded = true; // bool |
final isExternal = true; // bool |
final isFavorite = true; // bool |
final isMotion = true; // bool |
final isOffline = true; // bool |
final isReadOnly = true; // bool |
final isVisible = true; // bool |
final lensModel = lensModel_example; // String |
final libraryId = 38400000-8cf0-11bd-b23e-10b96e4ef00d; // String |
final make = make_example; // String |
final model = model_example; // String |
final page = 8.14; // num |
final size = 8.14; // num |
final state = state_example; // String |
final takenAfter = 2013-10-20T19:20:30+01:00; // DateTime |
final takenBefore = 2013-10-20T19:20:30+01:00; // DateTime |
final trashedAfter = 2013-10-20T19:20:30+01:00; // DateTime |
final trashedBefore = 2013-10-20T19:20:30+01:00; // DateTime |
final type = ; // AssetTypeEnum |
final updatedAfter = 2013-10-20T19:20:30+01:00; // DateTime |
final updatedBefore = 2013-10-20T19:20:30+01:00; // DateTime |
final withArchived = true; // bool |
final withDeleted = true; // bool |
final withExif = true; // bool |
try {
final result = api_instance.searchSmart(smartSearchDto);
final result = api_instance.searchSmart(query, city, country, createdAfter, createdBefore, deviceId, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, page, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, type, updatedAfter, updatedBefore, withArchived, withDeleted, withExif);
print(result);
} catch (e) {
print('Exception when calling SearchApi->searchSmart: $e\n');
@@ -354,7 +464,37 @@ try {
Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**smartSearchDto** | [**SmartSearchDto**](SmartSearchDto.md)| |
**query** | **String**| |
**city** | **String**| | [optional]
**country** | **String**| | [optional]
**createdAfter** | **DateTime**| | [optional]
**createdBefore** | **DateTime**| | [optional]
**deviceId** | **String**| | [optional]
**isArchived** | **bool**| | [optional]
**isEncoded** | **bool**| | [optional]
**isExternal** | **bool**| | [optional]
**isFavorite** | **bool**| | [optional]
**isMotion** | **bool**| | [optional]
**isOffline** | **bool**| | [optional]
**isReadOnly** | **bool**| | [optional]
**isVisible** | **bool**| | [optional]
**lensModel** | **String**| | [optional]
**libraryId** | **String**| | [optional]
**make** | **String**| | [optional]
**model** | **String**| | [optional]
**page** | **num**| | [optional]
**size** | **num**| | [optional]
**state** | **String**| | [optional]
**takenAfter** | **DateTime**| | [optional]
**takenBefore** | **DateTime**| | [optional]
**trashedAfter** | **DateTime**| | [optional]
**trashedBefore** | **DateTime**| | [optional]
**type** | [**AssetTypeEnum**](.md)| | [optional]
**updatedAfter** | **DateTime**| | [optional]
**updatedBefore** | **DateTime**| | [optional]
**withArchived** | **bool**| | [optional]
**withDeleted** | **bool**| | [optional]
**withExif** | **bool**| | [optional]
### Return type
@@ -366,7 +506,7 @@ Name | Type | Description | Notes
### HTTP request headers
- **Content-Type**: application/json
- **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)
-45
View File
@@ -1,45 +0,0 @@
# openapi.model.SmartSearchDto
## Load the model package
```dart
import 'package:openapi/api.dart';
```
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**city** | **String** | | [optional]
**country** | **String** | | [optional]
**createdAfter** | [**DateTime**](DateTime.md) | | [optional]
**createdBefore** | [**DateTime**](DateTime.md) | | [optional]
**deviceId** | **String** | | [optional]
**isArchived** | **bool** | | [optional]
**isEncoded** | **bool** | | [optional]
**isExternal** | **bool** | | [optional]
**isFavorite** | **bool** | | [optional]
**isMotion** | **bool** | | [optional]
**isOffline** | **bool** | | [optional]
**isReadOnly** | **bool** | | [optional]
**isVisible** | **bool** | | [optional]
**lensModel** | **String** | | [optional]
**libraryId** | **String** | | [optional]
**make** | **String** | | [optional]
**model** | **String** | | [optional]
**page** | **num** | | [optional]
**query** | **String** | |
**size** | **num** | | [optional]
**state** | **String** | | [optional]
**takenAfter** | [**DateTime**](DateTime.md) | | [optional]
**takenBefore** | [**DateTime**](DateTime.md) | | [optional]
**trashedAfter** | [**DateTime**](DateTime.md) | | [optional]
**trashedBefore** | [**DateTime**](DateTime.md) | | [optional]
**type** | [**AssetTypeEnum**](AssetTypeEnum.md) | | [optional]
**updatedAfter** | [**DateTime**](DateTime.md) | | [optional]
**updatedBefore** | [**DateTime**](DateTime.md) | | [optional]
**withArchived** | **bool** | | [optional]
**withDeleted** | **bool** | | [optional]
**withExif** | **bool** | | [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)
-2
View File
@@ -127,7 +127,6 @@ part 'model/map_marker_response_dto.dart';
part 'model/map_theme.dart';
part 'model/memory_lane_response_dto.dart';
part 'model/merge_person_dto.dart';
part 'model/metadata_search_dto.dart';
part 'model/model_type.dart';
part 'model/o_auth_authorize_response_dto.dart';
part 'model/o_auth_callback_dto.dart';
@@ -169,7 +168,6 @@ part 'model/shared_link_response_dto.dart';
part 'model/shared_link_type.dart';
part 'model/sign_up_dto.dart';
part 'model/smart_info_response_dto.dart';
part 'model/smart_search_dto.dart';
part 'model/system_config_dto.dart';
part 'model/system_config_f_fmpeg_dto.dart';
part 'model/system_config_job_dto.dart';
+6 -27
View File
@@ -652,9 +652,7 @@ class AssetApi {
/// * [bool] isArchived:
///
/// * [bool] isFavorite:
///
/// * [bool] withPartners:
Future<Response> getMapMarkersWithHttpInfo({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, bool? withPartners, }) async {
Future<Response> getMapMarkersWithHttpInfo({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, }) async {
// ignore: prefer_const_declarations
final path = r'/asset/map-marker';
@@ -677,9 +675,6 @@ class AssetApi {
if (isFavorite != null) {
queryParams.addAll(_queryParams('', 'isFavorite', isFavorite));
}
if (withPartners != null) {
queryParams.addAll(_queryParams('', 'withPartners', withPartners));
}
const contentTypes = <String>[];
@@ -704,10 +699,8 @@ class AssetApi {
/// * [bool] isArchived:
///
/// * [bool] isFavorite:
///
/// * [bool] withPartners:
Future<List<MapMarkerResponseDto>?> getMapMarkers({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, bool? withPartners, }) async {
final response = await getMapMarkersWithHttpInfo( fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, isArchived: isArchived, isFavorite: isFavorite, withPartners: withPartners, );
Future<List<MapMarkerResponseDto>?> getMapMarkers({ DateTime? fileCreatedAfter, DateTime? fileCreatedBefore, bool? isArchived, bool? isFavorite, }) async {
final response = await getMapMarkersWithHttpInfo( fileCreatedAfter: fileCreatedAfter, fileCreatedBefore: fileCreatedBefore, isArchived: isArchived, isFavorite: isFavorite, );
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -1140,8 +1133,6 @@ class AssetApi {
///
/// * [bool] isMotion:
///
/// * [bool] isNotInAlbum:
///
/// * [bool] isOffline:
///
/// * [bool] isReadOnly:
@@ -1164,8 +1155,6 @@ class AssetApi {
///
/// * [num] page:
///
/// * [List<String>] personIds:
///
/// * [String] resizePath:
///
/// * [num] size:
@@ -1197,7 +1186,7 @@ class AssetApi {
/// * [bool] withPeople:
///
/// * [bool] withStacked:
Future<Response> searchAssetsWithHttpInfo({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, List<String>? personIds, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async {
Future<Response> searchAssetsWithHttpInfo({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async {
// ignore: prefer_const_declarations
final path = r'/assets';
@@ -1250,9 +1239,6 @@ class AssetApi {
if (isMotion != null) {
queryParams.addAll(_queryParams('', 'isMotion', isMotion));
}
if (isNotInAlbum != null) {
queryParams.addAll(_queryParams('', 'isNotInAlbum', isNotInAlbum));
}
if (isOffline != null) {
queryParams.addAll(_queryParams('', 'isOffline', isOffline));
}
@@ -1286,9 +1272,6 @@ class AssetApi {
if (page != null) {
queryParams.addAll(_queryParams('', 'page', page));
}
if (personIds != null) {
queryParams.addAll(_queryParams('multi', 'personIds', personIds));
}
if (resizePath != null) {
queryParams.addAll(_queryParams('', 'resizePath', resizePath));
}
@@ -1382,8 +1365,6 @@ class AssetApi {
///
/// * [bool] isMotion:
///
/// * [bool] isNotInAlbum:
///
/// * [bool] isOffline:
///
/// * [bool] isReadOnly:
@@ -1406,8 +1387,6 @@ class AssetApi {
///
/// * [num] page:
///
/// * [List<String>] personIds:
///
/// * [String] resizePath:
///
/// * [num] size:
@@ -1439,8 +1418,8 @@ class AssetApi {
/// * [bool] withPeople:
///
/// * [bool] withStacked:
Future<List<AssetResponseDto>?> searchAssets({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isNotInAlbum, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, List<String>? personIds, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async {
final response = await searchAssetsWithHttpInfo( checksum: checksum, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceAssetId: deviceAssetId, deviceId: deviceId, encodedVideoPath: encodedVideoPath, id: id, isArchived: isArchived, isEncoded: isEncoded, isExternal: isExternal, isFavorite: isFavorite, isMotion: isMotion, isNotInAlbum: isNotInAlbum, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, lensModel: lensModel, libraryId: libraryId, make: make, model: model, order: order, originalFileName: originalFileName, originalPath: originalPath, page: page, personIds: personIds, resizePath: resizePath, size: size, state: state, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, webpPath: webpPath, withArchived: withArchived, withDeleted: withDeleted, withExif: withExif, withPeople: withPeople, withStacked: withStacked, );
Future<List<AssetResponseDto>?> searchAssets({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async {
final response = await searchAssetsWithHttpInfo( checksum: checksum, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceAssetId: deviceAssetId, deviceId: deviceId, encodedVideoPath: encodedVideoPath, id: id, isArchived: isArchived, isEncoded: isEncoded, isExternal: isExternal, isFavorite: isFavorite, isMotion: isMotion, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, lensModel: lensModel, libraryId: libraryId, make: make, model: model, order: order, originalFileName: originalFileName, originalPath: originalPath, page: page, resizePath: resizePath, size: size, state: state, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, webpPath: webpPath, withArchived: withArchived, withDeleted: withDeleted, withExif: withExif, withPeople: withPeople, withStacked: withStacked, );
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
+514 -18
View File
@@ -254,27 +254,231 @@ class SearchApi {
return null;
}
/// Performs an HTTP 'POST /search/metadata' operation and returns the [Response].
/// Performs an HTTP 'GET /search/metadata' operation and returns the [Response].
/// Parameters:
///
/// * [MetadataSearchDto] metadataSearchDto (required):
Future<Response> searchMetadataWithHttpInfo(MetadataSearchDto metadataSearchDto,) async {
/// * [String] checksum:
///
/// * [String] city:
///
/// * [String] country:
///
/// * [DateTime] createdAfter:
///
/// * [DateTime] createdBefore:
///
/// * [String] deviceAssetId:
///
/// * [String] deviceId:
///
/// * [String] encodedVideoPath:
///
/// * [String] id:
///
/// * [bool] isArchived:
///
/// * [bool] isEncoded:
///
/// * [bool] isExternal:
///
/// * [bool] isFavorite:
///
/// * [bool] isMotion:
///
/// * [bool] isOffline:
///
/// * [bool] isReadOnly:
///
/// * [bool] isVisible:
///
/// * [String] lensModel:
///
/// * [String] libraryId:
///
/// * [String] make:
///
/// * [String] model:
///
/// * [AssetOrder] order:
///
/// * [String] originalFileName:
///
/// * [String] originalPath:
///
/// * [num] page:
///
/// * [String] resizePath:
///
/// * [num] size:
///
/// * [String] state:
///
/// * [DateTime] takenAfter:
///
/// * [DateTime] takenBefore:
///
/// * [DateTime] trashedAfter:
///
/// * [DateTime] trashedBefore:
///
/// * [AssetTypeEnum] type:
///
/// * [DateTime] updatedAfter:
///
/// * [DateTime] updatedBefore:
///
/// * [String] webpPath:
///
/// * [bool] withArchived:
///
/// * [bool] withDeleted:
///
/// * [bool] withExif:
///
/// * [bool] withPeople:
///
/// * [bool] withStacked:
Future<Response> searchMetadataWithHttpInfo({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async {
// ignore: prefer_const_declarations
final path = r'/search/metadata';
// ignore: prefer_final_locals
Object? postBody = metadataSearchDto;
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>['application/json'];
if (checksum != null) {
queryParams.addAll(_queryParams('', 'checksum', checksum));
}
if (city != null) {
queryParams.addAll(_queryParams('', 'city', city));
}
if (country != null) {
queryParams.addAll(_queryParams('', 'country', country));
}
if (createdAfter != null) {
queryParams.addAll(_queryParams('', 'createdAfter', createdAfter));
}
if (createdBefore != null) {
queryParams.addAll(_queryParams('', 'createdBefore', createdBefore));
}
if (deviceAssetId != null) {
queryParams.addAll(_queryParams('', 'deviceAssetId', deviceAssetId));
}
if (deviceId != null) {
queryParams.addAll(_queryParams('', 'deviceId', deviceId));
}
if (encodedVideoPath != null) {
queryParams.addAll(_queryParams('', 'encodedVideoPath', encodedVideoPath));
}
if (id != null) {
queryParams.addAll(_queryParams('', 'id', id));
}
if (isArchived != null) {
queryParams.addAll(_queryParams('', 'isArchived', isArchived));
}
if (isEncoded != null) {
queryParams.addAll(_queryParams('', 'isEncoded', isEncoded));
}
if (isExternal != null) {
queryParams.addAll(_queryParams('', 'isExternal', isExternal));
}
if (isFavorite != null) {
queryParams.addAll(_queryParams('', 'isFavorite', isFavorite));
}
if (isMotion != null) {
queryParams.addAll(_queryParams('', 'isMotion', isMotion));
}
if (isOffline != null) {
queryParams.addAll(_queryParams('', 'isOffline', isOffline));
}
if (isReadOnly != null) {
queryParams.addAll(_queryParams('', 'isReadOnly', isReadOnly));
}
if (isVisible != null) {
queryParams.addAll(_queryParams('', 'isVisible', isVisible));
}
if (lensModel != null) {
queryParams.addAll(_queryParams('', 'lensModel', lensModel));
}
if (libraryId != null) {
queryParams.addAll(_queryParams('', 'libraryId', libraryId));
}
if (make != null) {
queryParams.addAll(_queryParams('', 'make', make));
}
if (model != null) {
queryParams.addAll(_queryParams('', 'model', model));
}
if (order != null) {
queryParams.addAll(_queryParams('', 'order', order));
}
if (originalFileName != null) {
queryParams.addAll(_queryParams('', 'originalFileName', originalFileName));
}
if (originalPath != null) {
queryParams.addAll(_queryParams('', 'originalPath', originalPath));
}
if (page != null) {
queryParams.addAll(_queryParams('', 'page', page));
}
if (resizePath != null) {
queryParams.addAll(_queryParams('', 'resizePath', resizePath));
}
if (size != null) {
queryParams.addAll(_queryParams('', 'size', size));
}
if (state != null) {
queryParams.addAll(_queryParams('', 'state', state));
}
if (takenAfter != null) {
queryParams.addAll(_queryParams('', 'takenAfter', takenAfter));
}
if (takenBefore != null) {
queryParams.addAll(_queryParams('', 'takenBefore', takenBefore));
}
if (trashedAfter != null) {
queryParams.addAll(_queryParams('', 'trashedAfter', trashedAfter));
}
if (trashedBefore != null) {
queryParams.addAll(_queryParams('', 'trashedBefore', trashedBefore));
}
if (type != null) {
queryParams.addAll(_queryParams('', 'type', type));
}
if (updatedAfter != null) {
queryParams.addAll(_queryParams('', 'updatedAfter', updatedAfter));
}
if (updatedBefore != null) {
queryParams.addAll(_queryParams('', 'updatedBefore', updatedBefore));
}
if (webpPath != null) {
queryParams.addAll(_queryParams('', 'webpPath', webpPath));
}
if (withArchived != null) {
queryParams.addAll(_queryParams('', 'withArchived', withArchived));
}
if (withDeleted != null) {
queryParams.addAll(_queryParams('', 'withDeleted', withDeleted));
}
if (withExif != null) {
queryParams.addAll(_queryParams('', 'withExif', withExif));
}
if (withPeople != null) {
queryParams.addAll(_queryParams('', 'withPeople', withPeople));
}
if (withStacked != null) {
queryParams.addAll(_queryParams('', 'withStacked', withStacked));
}
const contentTypes = <String>[];
return apiClient.invokeAPI(
path,
'POST',
'GET',
queryParams,
postBody,
headerParams,
@@ -285,9 +489,89 @@ class SearchApi {
/// Parameters:
///
/// * [MetadataSearchDto] metadataSearchDto (required):
Future<SearchResponseDto?> searchMetadata(MetadataSearchDto metadataSearchDto,) async {
final response = await searchMetadataWithHttpInfo(metadataSearchDto,);
/// * [String] checksum:
///
/// * [String] city:
///
/// * [String] country:
///
/// * [DateTime] createdAfter:
///
/// * [DateTime] createdBefore:
///
/// * [String] deviceAssetId:
///
/// * [String] deviceId:
///
/// * [String] encodedVideoPath:
///
/// * [String] id:
///
/// * [bool] isArchived:
///
/// * [bool] isEncoded:
///
/// * [bool] isExternal:
///
/// * [bool] isFavorite:
///
/// * [bool] isMotion:
///
/// * [bool] isOffline:
///
/// * [bool] isReadOnly:
///
/// * [bool] isVisible:
///
/// * [String] lensModel:
///
/// * [String] libraryId:
///
/// * [String] make:
///
/// * [String] model:
///
/// * [AssetOrder] order:
///
/// * [String] originalFileName:
///
/// * [String] originalPath:
///
/// * [num] page:
///
/// * [String] resizePath:
///
/// * [num] size:
///
/// * [String] state:
///
/// * [DateTime] takenAfter:
///
/// * [DateTime] takenBefore:
///
/// * [DateTime] trashedAfter:
///
/// * [DateTime] trashedBefore:
///
/// * [AssetTypeEnum] type:
///
/// * [DateTime] updatedAfter:
///
/// * [DateTime] updatedBefore:
///
/// * [String] webpPath:
///
/// * [bool] withArchived:
///
/// * [bool] withDeleted:
///
/// * [bool] withExif:
///
/// * [bool] withPeople:
///
/// * [bool] withStacked:
Future<SearchResponseDto?> searchMetadata({ String? checksum, String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceAssetId, String? deviceId, String? encodedVideoPath, String? id, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, AssetOrder? order, String? originalFileName, String? originalPath, num? page, String? resizePath, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, String? webpPath, bool? withArchived, bool? withDeleted, bool? withExif, bool? withPeople, bool? withStacked, }) async {
final response = await searchMetadataWithHttpInfo( checksum: checksum, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceAssetId: deviceAssetId, deviceId: deviceId, encodedVideoPath: encodedVideoPath, id: id, isArchived: isArchived, isEncoded: isEncoded, isExternal: isExternal, isFavorite: isFavorite, isMotion: isMotion, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, lensModel: lensModel, libraryId: libraryId, make: make, model: model, order: order, originalFileName: originalFileName, originalPath: originalPath, page: page, resizePath: resizePath, size: size, state: state, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, webpPath: webpPath, withArchived: withArchived, withDeleted: withDeleted, withExif: withExif, withPeople: withPeople, withStacked: withStacked, );
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
@@ -360,27 +644,179 @@ class SearchApi {
return null;
}
/// Performs an HTTP 'POST /search/smart' operation and returns the [Response].
/// Performs an HTTP 'GET /search/smart' operation and returns the [Response].
/// Parameters:
///
/// * [SmartSearchDto] smartSearchDto (required):
Future<Response> searchSmartWithHttpInfo(SmartSearchDto smartSearchDto,) async {
/// * [String] query (required):
///
/// * [String] city:
///
/// * [String] country:
///
/// * [DateTime] createdAfter:
///
/// * [DateTime] createdBefore:
///
/// * [String] deviceId:
///
/// * [bool] isArchived:
///
/// * [bool] isEncoded:
///
/// * [bool] isExternal:
///
/// * [bool] isFavorite:
///
/// * [bool] isMotion:
///
/// * [bool] isOffline:
///
/// * [bool] isReadOnly:
///
/// * [bool] isVisible:
///
/// * [String] lensModel:
///
/// * [String] libraryId:
///
/// * [String] make:
///
/// * [String] model:
///
/// * [num] page:
///
/// * [num] size:
///
/// * [String] state:
///
/// * [DateTime] takenAfter:
///
/// * [DateTime] takenBefore:
///
/// * [DateTime] trashedAfter:
///
/// * [DateTime] trashedBefore:
///
/// * [AssetTypeEnum] type:
///
/// * [DateTime] updatedAfter:
///
/// * [DateTime] updatedBefore:
///
/// * [bool] withArchived:
///
/// * [bool] withDeleted:
///
/// * [bool] withExif:
Future<Response> searchSmartWithHttpInfo(String query, { String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceId, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, num? page, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, bool? withArchived, bool? withDeleted, bool? withExif, }) async {
// ignore: prefer_const_declarations
final path = r'/search/smart';
// ignore: prefer_final_locals
Object? postBody = smartSearchDto;
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>['application/json'];
if (city != null) {
queryParams.addAll(_queryParams('', 'city', city));
}
if (country != null) {
queryParams.addAll(_queryParams('', 'country', country));
}
if (createdAfter != null) {
queryParams.addAll(_queryParams('', 'createdAfter', createdAfter));
}
if (createdBefore != null) {
queryParams.addAll(_queryParams('', 'createdBefore', createdBefore));
}
if (deviceId != null) {
queryParams.addAll(_queryParams('', 'deviceId', deviceId));
}
if (isArchived != null) {
queryParams.addAll(_queryParams('', 'isArchived', isArchived));
}
if (isEncoded != null) {
queryParams.addAll(_queryParams('', 'isEncoded', isEncoded));
}
if (isExternal != null) {
queryParams.addAll(_queryParams('', 'isExternal', isExternal));
}
if (isFavorite != null) {
queryParams.addAll(_queryParams('', 'isFavorite', isFavorite));
}
if (isMotion != null) {
queryParams.addAll(_queryParams('', 'isMotion', isMotion));
}
if (isOffline != null) {
queryParams.addAll(_queryParams('', 'isOffline', isOffline));
}
if (isReadOnly != null) {
queryParams.addAll(_queryParams('', 'isReadOnly', isReadOnly));
}
if (isVisible != null) {
queryParams.addAll(_queryParams('', 'isVisible', isVisible));
}
if (lensModel != null) {
queryParams.addAll(_queryParams('', 'lensModel', lensModel));
}
if (libraryId != null) {
queryParams.addAll(_queryParams('', 'libraryId', libraryId));
}
if (make != null) {
queryParams.addAll(_queryParams('', 'make', make));
}
if (model != null) {
queryParams.addAll(_queryParams('', 'model', model));
}
if (page != null) {
queryParams.addAll(_queryParams('', 'page', page));
}
queryParams.addAll(_queryParams('', 'query', query));
if (size != null) {
queryParams.addAll(_queryParams('', 'size', size));
}
if (state != null) {
queryParams.addAll(_queryParams('', 'state', state));
}
if (takenAfter != null) {
queryParams.addAll(_queryParams('', 'takenAfter', takenAfter));
}
if (takenBefore != null) {
queryParams.addAll(_queryParams('', 'takenBefore', takenBefore));
}
if (trashedAfter != null) {
queryParams.addAll(_queryParams('', 'trashedAfter', trashedAfter));
}
if (trashedBefore != null) {
queryParams.addAll(_queryParams('', 'trashedBefore', trashedBefore));
}
if (type != null) {
queryParams.addAll(_queryParams('', 'type', type));
}
if (updatedAfter != null) {
queryParams.addAll(_queryParams('', 'updatedAfter', updatedAfter));
}
if (updatedBefore != null) {
queryParams.addAll(_queryParams('', 'updatedBefore', updatedBefore));
}
if (withArchived != null) {
queryParams.addAll(_queryParams('', 'withArchived', withArchived));
}
if (withDeleted != null) {
queryParams.addAll(_queryParams('', 'withDeleted', withDeleted));
}
if (withExif != null) {
queryParams.addAll(_queryParams('', 'withExif', withExif));
}
const contentTypes = <String>[];
return apiClient.invokeAPI(
path,
'POST',
'GET',
queryParams,
postBody,
headerParams,
@@ -391,9 +827,69 @@ class SearchApi {
/// Parameters:
///
/// * [SmartSearchDto] smartSearchDto (required):
Future<SearchResponseDto?> searchSmart(SmartSearchDto smartSearchDto,) async {
final response = await searchSmartWithHttpInfo(smartSearchDto,);
/// * [String] query (required):
///
/// * [String] city:
///
/// * [String] country:
///
/// * [DateTime] createdAfter:
///
/// * [DateTime] createdBefore:
///
/// * [String] deviceId:
///
/// * [bool] isArchived:
///
/// * [bool] isEncoded:
///
/// * [bool] isExternal:
///
/// * [bool] isFavorite:
///
/// * [bool] isMotion:
///
/// * [bool] isOffline:
///
/// * [bool] isReadOnly:
///
/// * [bool] isVisible:
///
/// * [String] lensModel:
///
/// * [String] libraryId:
///
/// * [String] make:
///
/// * [String] model:
///
/// * [num] page:
///
/// * [num] size:
///
/// * [String] state:
///
/// * [DateTime] takenAfter:
///
/// * [DateTime] takenBefore:
///
/// * [DateTime] trashedAfter:
///
/// * [DateTime] trashedBefore:
///
/// * [AssetTypeEnum] type:
///
/// * [DateTime] updatedAfter:
///
/// * [DateTime] updatedBefore:
///
/// * [bool] withArchived:
///
/// * [bool] withDeleted:
///
/// * [bool] withExif:
Future<SearchResponseDto?> searchSmart(String query, { String? city, String? country, DateTime? createdAfter, DateTime? createdBefore, String? deviceId, bool? isArchived, bool? isEncoded, bool? isExternal, bool? isFavorite, bool? isMotion, bool? isOffline, bool? isReadOnly, bool? isVisible, String? lensModel, String? libraryId, String? make, String? model, num? page, num? size, String? state, DateTime? takenAfter, DateTime? takenBefore, DateTime? trashedAfter, DateTime? trashedBefore, AssetTypeEnum? type, DateTime? updatedAfter, DateTime? updatedBefore, bool? withArchived, bool? withDeleted, bool? withExif, }) async {
final response = await searchSmartWithHttpInfo(query, city: city, country: country, createdAfter: createdAfter, createdBefore: createdBefore, deviceId: deviceId, isArchived: isArchived, isEncoded: isEncoded, isExternal: isExternal, isFavorite: isFavorite, isMotion: isMotion, isOffline: isOffline, isReadOnly: isReadOnly, isVisible: isVisible, lensModel: lensModel, libraryId: libraryId, make: make, model: model, page: page, size: size, state: state, takenAfter: takenAfter, takenBefore: takenBefore, trashedAfter: trashedAfter, trashedBefore: trashedBefore, type: type, updatedAfter: updatedAfter, updatedBefore: updatedBefore, withArchived: withArchived, withDeleted: withDeleted, withExif: withExif, );
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
-4
View File
@@ -336,8 +336,6 @@ class ApiClient {
return MemoryLaneResponseDto.fromJson(value);
case 'MergePersonDto':
return MergePersonDto.fromJson(value);
case 'MetadataSearchDto':
return MetadataSearchDto.fromJson(value);
case 'ModelType':
return ModelTypeTypeTransformer().decode(value);
case 'OAuthAuthorizeResponseDto':
@@ -420,8 +418,6 @@ class ApiClient {
return SignUpDto.fromJson(value);
case 'SmartInfoResponseDto':
return SmartInfoResponseDto.fromJson(value);
case 'SmartSearchDto':
return SmartSearchDto.fromJson(value);
case 'SystemConfigDto':
return SystemConfigDto.fromJson(value);
case 'SystemConfigFFmpegDto':
-813
View File
@@ -1,813 +0,0 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class MetadataSearchDto {
/// Returns a new [MetadataSearchDto] instance.
MetadataSearchDto({
this.checksum,
this.city,
this.country,
this.createdAfter,
this.createdBefore,
this.deviceAssetId,
this.deviceId,
this.encodedVideoPath,
this.id,
this.isArchived,
this.isEncoded,
this.isExternal,
this.isFavorite,
this.isMotion,
this.isNotInAlbum,
this.isOffline,
this.isReadOnly,
this.isVisible,
this.lensModel,
this.libraryId,
this.make,
this.model,
this.order,
this.originalFileName,
this.originalPath,
this.page,
this.personIds = const [],
this.resizePath,
this.size,
this.state,
this.takenAfter,
this.takenBefore,
this.trashedAfter,
this.trashedBefore,
this.type,
this.updatedAfter,
this.updatedBefore,
this.webpPath,
this.withArchived,
this.withDeleted,
this.withExif,
this.withPeople,
this.withStacked,
});
///
/// 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? checksum;
///
/// 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? city;
///
/// 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? country;
///
/// 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.
///
DateTime? createdAfter;
///
/// 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.
///
DateTime? createdBefore;
///
/// 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? deviceAssetId;
///
/// 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? deviceId;
///
/// 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? encodedVideoPath;
///
/// 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? id;
///
/// 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? isArchived;
///
/// 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? isEncoded;
///
/// 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? isExternal;
///
/// 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? isFavorite;
///
/// 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? isMotion;
///
/// 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? isNotInAlbum;
///
/// 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? isOffline;
///
/// 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? isReadOnly;
///
/// 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? isVisible;
///
/// 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? lensModel;
///
/// 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? libraryId;
///
/// 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? make;
///
/// 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? model;
///
/// 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.
///
AssetOrder? order;
///
/// 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? originalFileName;
///
/// 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? originalPath;
///
/// 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.
///
num? page;
List<String> personIds;
///
/// 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? resizePath;
///
/// 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.
///
num? size;
///
/// 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? state;
///
/// 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.
///
DateTime? takenAfter;
///
/// 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.
///
DateTime? takenBefore;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
DateTime? trashedAfter;
///
/// 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.
///
DateTime? trashedBefore;
///
/// 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.
///
AssetTypeEnum? type;
///
/// 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.
///
DateTime? updatedAfter;
///
/// 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.
///
DateTime? updatedBefore;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
String? webpPath;
///
/// 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? withArchived;
///
/// 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? withDeleted;
///
/// 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? withExif;
///
/// 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? withPeople;
///
/// 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? withStacked;
@override
bool operator ==(Object other) => identical(this, other) || other is MetadataSearchDto &&
other.checksum == checksum &&
other.city == city &&
other.country == country &&
other.createdAfter == createdAfter &&
other.createdBefore == createdBefore &&
other.deviceAssetId == deviceAssetId &&
other.deviceId == deviceId &&
other.encodedVideoPath == encodedVideoPath &&
other.id == id &&
other.isArchived == isArchived &&
other.isEncoded == isEncoded &&
other.isExternal == isExternal &&
other.isFavorite == isFavorite &&
other.isMotion == isMotion &&
other.isNotInAlbum == isNotInAlbum &&
other.isOffline == isOffline &&
other.isReadOnly == isReadOnly &&
other.isVisible == isVisible &&
other.lensModel == lensModel &&
other.libraryId == libraryId &&
other.make == make &&
other.model == model &&
other.order == order &&
other.originalFileName == originalFileName &&
other.originalPath == originalPath &&
other.page == page &&
_deepEquality.equals(other.personIds, personIds) &&
other.resizePath == resizePath &&
other.size == size &&
other.state == state &&
other.takenAfter == takenAfter &&
other.takenBefore == takenBefore &&
other.trashedAfter == trashedAfter &&
other.trashedBefore == trashedBefore &&
other.type == type &&
other.updatedAfter == updatedAfter &&
other.updatedBefore == updatedBefore &&
other.webpPath == webpPath &&
other.withArchived == withArchived &&
other.withDeleted == withDeleted &&
other.withExif == withExif &&
other.withPeople == withPeople &&
other.withStacked == withStacked;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(checksum == null ? 0 : checksum!.hashCode) +
(city == null ? 0 : city!.hashCode) +
(country == null ? 0 : country!.hashCode) +
(createdAfter == null ? 0 : createdAfter!.hashCode) +
(createdBefore == null ? 0 : createdBefore!.hashCode) +
(deviceAssetId == null ? 0 : deviceAssetId!.hashCode) +
(deviceId == null ? 0 : deviceId!.hashCode) +
(encodedVideoPath == null ? 0 : encodedVideoPath!.hashCode) +
(id == null ? 0 : id!.hashCode) +
(isArchived == null ? 0 : isArchived!.hashCode) +
(isEncoded == null ? 0 : isEncoded!.hashCode) +
(isExternal == null ? 0 : isExternal!.hashCode) +
(isFavorite == null ? 0 : isFavorite!.hashCode) +
(isMotion == null ? 0 : isMotion!.hashCode) +
(isNotInAlbum == null ? 0 : isNotInAlbum!.hashCode) +
(isOffline == null ? 0 : isOffline!.hashCode) +
(isReadOnly == null ? 0 : isReadOnly!.hashCode) +
(isVisible == null ? 0 : isVisible!.hashCode) +
(lensModel == null ? 0 : lensModel!.hashCode) +
(libraryId == null ? 0 : libraryId!.hashCode) +
(make == null ? 0 : make!.hashCode) +
(model == null ? 0 : model!.hashCode) +
(order == null ? 0 : order!.hashCode) +
(originalFileName == null ? 0 : originalFileName!.hashCode) +
(originalPath == null ? 0 : originalPath!.hashCode) +
(page == null ? 0 : page!.hashCode) +
(personIds.hashCode) +
(resizePath == null ? 0 : resizePath!.hashCode) +
(size == null ? 0 : size!.hashCode) +
(state == null ? 0 : state!.hashCode) +
(takenAfter == null ? 0 : takenAfter!.hashCode) +
(takenBefore == null ? 0 : takenBefore!.hashCode) +
(trashedAfter == null ? 0 : trashedAfter!.hashCode) +
(trashedBefore == null ? 0 : trashedBefore!.hashCode) +
(type == null ? 0 : type!.hashCode) +
(updatedAfter == null ? 0 : updatedAfter!.hashCode) +
(updatedBefore == null ? 0 : updatedBefore!.hashCode) +
(webpPath == null ? 0 : webpPath!.hashCode) +
(withArchived == null ? 0 : withArchived!.hashCode) +
(withDeleted == null ? 0 : withDeleted!.hashCode) +
(withExif == null ? 0 : withExif!.hashCode) +
(withPeople == null ? 0 : withPeople!.hashCode) +
(withStacked == null ? 0 : withStacked!.hashCode);
@override
String toString() => 'MetadataSearchDto[checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceAssetId=$deviceAssetId, deviceId=$deviceId, encodedVideoPath=$encodedVideoPath, id=$id, isArchived=$isArchived, isEncoded=$isEncoded, isExternal=$isExternal, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isReadOnly=$isReadOnly, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, order=$order, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, resizePath=$resizePath, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, webpPath=$webpPath, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
if (this.checksum != null) {
json[r'checksum'] = this.checksum;
} else {
// json[r'checksum'] = null;
}
if (this.city != null) {
json[r'city'] = this.city;
} else {
// json[r'city'] = null;
}
if (this.country != null) {
json[r'country'] = this.country;
} else {
// json[r'country'] = null;
}
if (this.createdAfter != null) {
json[r'createdAfter'] = this.createdAfter!.toUtc().toIso8601String();
} else {
// json[r'createdAfter'] = null;
}
if (this.createdBefore != null) {
json[r'createdBefore'] = this.createdBefore!.toUtc().toIso8601String();
} else {
// json[r'createdBefore'] = null;
}
if (this.deviceAssetId != null) {
json[r'deviceAssetId'] = this.deviceAssetId;
} else {
// json[r'deviceAssetId'] = null;
}
if (this.deviceId != null) {
json[r'deviceId'] = this.deviceId;
} else {
// json[r'deviceId'] = null;
}
if (this.encodedVideoPath != null) {
json[r'encodedVideoPath'] = this.encodedVideoPath;
} else {
// json[r'encodedVideoPath'] = null;
}
if (this.id != null) {
json[r'id'] = this.id;
} else {
// json[r'id'] = null;
}
if (this.isArchived != null) {
json[r'isArchived'] = this.isArchived;
} else {
// json[r'isArchived'] = null;
}
if (this.isEncoded != null) {
json[r'isEncoded'] = this.isEncoded;
} else {
// json[r'isEncoded'] = null;
}
if (this.isExternal != null) {
json[r'isExternal'] = this.isExternal;
} else {
// json[r'isExternal'] = null;
}
if (this.isFavorite != null) {
json[r'isFavorite'] = this.isFavorite;
} else {
// json[r'isFavorite'] = null;
}
if (this.isMotion != null) {
json[r'isMotion'] = this.isMotion;
} else {
// json[r'isMotion'] = null;
}
if (this.isNotInAlbum != null) {
json[r'isNotInAlbum'] = this.isNotInAlbum;
} else {
// json[r'isNotInAlbum'] = null;
}
if (this.isOffline != null) {
json[r'isOffline'] = this.isOffline;
} else {
// json[r'isOffline'] = null;
}
if (this.isReadOnly != null) {
json[r'isReadOnly'] = this.isReadOnly;
} else {
// json[r'isReadOnly'] = null;
}
if (this.isVisible != null) {
json[r'isVisible'] = this.isVisible;
} else {
// json[r'isVisible'] = null;
}
if (this.lensModel != null) {
json[r'lensModel'] = this.lensModel;
} else {
// json[r'lensModel'] = null;
}
if (this.libraryId != null) {
json[r'libraryId'] = this.libraryId;
} else {
// json[r'libraryId'] = null;
}
if (this.make != null) {
json[r'make'] = this.make;
} else {
// json[r'make'] = null;
}
if (this.model != null) {
json[r'model'] = this.model;
} else {
// json[r'model'] = null;
}
if (this.order != null) {
json[r'order'] = this.order;
} else {
// json[r'order'] = null;
}
if (this.originalFileName != null) {
json[r'originalFileName'] = this.originalFileName;
} else {
// json[r'originalFileName'] = null;
}
if (this.originalPath != null) {
json[r'originalPath'] = this.originalPath;
} else {
// json[r'originalPath'] = null;
}
if (this.page != null) {
json[r'page'] = this.page;
} else {
// json[r'page'] = null;
}
json[r'personIds'] = this.personIds;
if (this.resizePath != null) {
json[r'resizePath'] = this.resizePath;
} else {
// json[r'resizePath'] = null;
}
if (this.size != null) {
json[r'size'] = this.size;
} else {
// json[r'size'] = null;
}
if (this.state != null) {
json[r'state'] = this.state;
} else {
// json[r'state'] = null;
}
if (this.takenAfter != null) {
json[r'takenAfter'] = this.takenAfter!.toUtc().toIso8601String();
} else {
// json[r'takenAfter'] = null;
}
if (this.takenBefore != null) {
json[r'takenBefore'] = this.takenBefore!.toUtc().toIso8601String();
} else {
// json[r'takenBefore'] = null;
}
if (this.trashedAfter != null) {
json[r'trashedAfter'] = this.trashedAfter!.toUtc().toIso8601String();
} else {
// json[r'trashedAfter'] = null;
}
if (this.trashedBefore != null) {
json[r'trashedBefore'] = this.trashedBefore!.toUtc().toIso8601String();
} else {
// json[r'trashedBefore'] = null;
}
if (this.type != null) {
json[r'type'] = this.type;
} else {
// json[r'type'] = null;
}
if (this.updatedAfter != null) {
json[r'updatedAfter'] = this.updatedAfter!.toUtc().toIso8601String();
} else {
// json[r'updatedAfter'] = null;
}
if (this.updatedBefore != null) {
json[r'updatedBefore'] = this.updatedBefore!.toUtc().toIso8601String();
} else {
// json[r'updatedBefore'] = null;
}
if (this.webpPath != null) {
json[r'webpPath'] = this.webpPath;
} else {
// json[r'webpPath'] = null;
}
if (this.withArchived != null) {
json[r'withArchived'] = this.withArchived;
} else {
// json[r'withArchived'] = null;
}
if (this.withDeleted != null) {
json[r'withDeleted'] = this.withDeleted;
} else {
// json[r'withDeleted'] = null;
}
if (this.withExif != null) {
json[r'withExif'] = this.withExif;
} else {
// json[r'withExif'] = null;
}
if (this.withPeople != null) {
json[r'withPeople'] = this.withPeople;
} else {
// json[r'withPeople'] = null;
}
if (this.withStacked != null) {
json[r'withStacked'] = this.withStacked;
} else {
// json[r'withStacked'] = null;
}
return json;
}
/// Returns a new [MetadataSearchDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static MetadataSearchDto? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return MetadataSearchDto(
checksum: mapValueOfType<String>(json, r'checksum'),
city: mapValueOfType<String>(json, r'city'),
country: mapValueOfType<String>(json, r'country'),
createdAfter: mapDateTime(json, r'createdAfter', r''),
createdBefore: mapDateTime(json, r'createdBefore', r''),
deviceAssetId: mapValueOfType<String>(json, r'deviceAssetId'),
deviceId: mapValueOfType<String>(json, r'deviceId'),
encodedVideoPath: mapValueOfType<String>(json, r'encodedVideoPath'),
id: mapValueOfType<String>(json, r'id'),
isArchived: mapValueOfType<bool>(json, r'isArchived'),
isEncoded: mapValueOfType<bool>(json, r'isEncoded'),
isExternal: mapValueOfType<bool>(json, r'isExternal'),
isFavorite: mapValueOfType<bool>(json, r'isFavorite'),
isMotion: mapValueOfType<bool>(json, r'isMotion'),
isNotInAlbum: mapValueOfType<bool>(json, r'isNotInAlbum'),
isOffline: mapValueOfType<bool>(json, r'isOffline'),
isReadOnly: mapValueOfType<bool>(json, r'isReadOnly'),
isVisible: mapValueOfType<bool>(json, r'isVisible'),
lensModel: mapValueOfType<String>(json, r'lensModel'),
libraryId: mapValueOfType<String>(json, r'libraryId'),
make: mapValueOfType<String>(json, r'make'),
model: mapValueOfType<String>(json, r'model'),
order: AssetOrder.fromJson(json[r'order']),
originalFileName: mapValueOfType<String>(json, r'originalFileName'),
originalPath: mapValueOfType<String>(json, r'originalPath'),
page: num.parse('${json[r'page']}'),
personIds: json[r'personIds'] is Iterable
? (json[r'personIds'] as Iterable).cast<String>().toList(growable: false)
: const [],
resizePath: mapValueOfType<String>(json, r'resizePath'),
size: num.parse('${json[r'size']}'),
state: mapValueOfType<String>(json, r'state'),
takenAfter: mapDateTime(json, r'takenAfter', r''),
takenBefore: mapDateTime(json, r'takenBefore', r''),
trashedAfter: mapDateTime(json, r'trashedAfter', r''),
trashedBefore: mapDateTime(json, r'trashedBefore', r''),
type: AssetTypeEnum.fromJson(json[r'type']),
updatedAfter: mapDateTime(json, r'updatedAfter', r''),
updatedBefore: mapDateTime(json, r'updatedBefore', r''),
webpPath: mapValueOfType<String>(json, r'webpPath'),
withArchived: mapValueOfType<bool>(json, r'withArchived'),
withDeleted: mapValueOfType<bool>(json, r'withDeleted'),
withExif: mapValueOfType<bool>(json, r'withExif'),
withPeople: mapValueOfType<bool>(json, r'withPeople'),
withStacked: mapValueOfType<bool>(json, r'withStacked'),
);
}
return null;
}
static List<MetadataSearchDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <MetadataSearchDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = MetadataSearchDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, MetadataSearchDto> mapFromJson(dynamic json) {
final map = <String, MetadataSearchDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = MetadataSearchDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of MetadataSearchDto-objects as value to a dart map
static Map<String, List<MetadataSearchDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<MetadataSearchDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = MetadataSearchDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
};
}
-608
View File
@@ -1,608 +0,0 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class SmartSearchDto {
/// Returns a new [SmartSearchDto] instance.
SmartSearchDto({
this.city,
this.country,
this.createdAfter,
this.createdBefore,
this.deviceId,
this.isArchived,
this.isEncoded,
this.isExternal,
this.isFavorite,
this.isMotion,
this.isOffline,
this.isReadOnly,
this.isVisible,
this.lensModel,
this.libraryId,
this.make,
this.model,
this.page,
required this.query,
this.size,
this.state,
this.takenAfter,
this.takenBefore,
this.trashedAfter,
this.trashedBefore,
this.type,
this.updatedAfter,
this.updatedBefore,
this.withArchived,
this.withDeleted,
this.withExif,
});
///
/// 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? city;
///
/// 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? country;
///
/// 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.
///
DateTime? createdAfter;
///
/// 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.
///
DateTime? createdBefore;
///
/// 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? deviceId;
///
/// 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? isArchived;
///
/// 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? isEncoded;
///
/// 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? isExternal;
///
/// 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? isFavorite;
///
/// 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? isMotion;
///
/// 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? isOffline;
///
/// 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? isReadOnly;
///
/// 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? isVisible;
///
/// 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? lensModel;
///
/// 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? libraryId;
///
/// 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? make;
///
/// 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? model;
///
/// 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.
///
num? page;
String query;
///
/// 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.
///
num? size;
///
/// 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? state;
///
/// 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.
///
DateTime? takenAfter;
///
/// 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.
///
DateTime? takenBefore;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
DateTime? trashedAfter;
///
/// 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.
///
DateTime? trashedBefore;
///
/// 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.
///
AssetTypeEnum? type;
///
/// 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.
///
DateTime? updatedAfter;
///
/// 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.
///
DateTime? updatedBefore;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
bool? withArchived;
///
/// 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? withDeleted;
///
/// 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? withExif;
@override
bool operator ==(Object other) => identical(this, other) || other is SmartSearchDto &&
other.city == city &&
other.country == country &&
other.createdAfter == createdAfter &&
other.createdBefore == createdBefore &&
other.deviceId == deviceId &&
other.isArchived == isArchived &&
other.isEncoded == isEncoded &&
other.isExternal == isExternal &&
other.isFavorite == isFavorite &&
other.isMotion == isMotion &&
other.isOffline == isOffline &&
other.isReadOnly == isReadOnly &&
other.isVisible == isVisible &&
other.lensModel == lensModel &&
other.libraryId == libraryId &&
other.make == make &&
other.model == model &&
other.page == page &&
other.query == query &&
other.size == size &&
other.state == state &&
other.takenAfter == takenAfter &&
other.takenBefore == takenBefore &&
other.trashedAfter == trashedAfter &&
other.trashedBefore == trashedBefore &&
other.type == type &&
other.updatedAfter == updatedAfter &&
other.updatedBefore == updatedBefore &&
other.withArchived == withArchived &&
other.withDeleted == withDeleted &&
other.withExif == withExif;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(city == null ? 0 : city!.hashCode) +
(country == null ? 0 : country!.hashCode) +
(createdAfter == null ? 0 : createdAfter!.hashCode) +
(createdBefore == null ? 0 : createdBefore!.hashCode) +
(deviceId == null ? 0 : deviceId!.hashCode) +
(isArchived == null ? 0 : isArchived!.hashCode) +
(isEncoded == null ? 0 : isEncoded!.hashCode) +
(isExternal == null ? 0 : isExternal!.hashCode) +
(isFavorite == null ? 0 : isFavorite!.hashCode) +
(isMotion == null ? 0 : isMotion!.hashCode) +
(isOffline == null ? 0 : isOffline!.hashCode) +
(isReadOnly == null ? 0 : isReadOnly!.hashCode) +
(isVisible == null ? 0 : isVisible!.hashCode) +
(lensModel == null ? 0 : lensModel!.hashCode) +
(libraryId == null ? 0 : libraryId!.hashCode) +
(make == null ? 0 : make!.hashCode) +
(model == null ? 0 : model!.hashCode) +
(page == null ? 0 : page!.hashCode) +
(query.hashCode) +
(size == null ? 0 : size!.hashCode) +
(state == null ? 0 : state!.hashCode) +
(takenAfter == null ? 0 : takenAfter!.hashCode) +
(takenBefore == null ? 0 : takenBefore!.hashCode) +
(trashedAfter == null ? 0 : trashedAfter!.hashCode) +
(trashedBefore == null ? 0 : trashedBefore!.hashCode) +
(type == null ? 0 : type!.hashCode) +
(updatedAfter == null ? 0 : updatedAfter!.hashCode) +
(updatedBefore == null ? 0 : updatedBefore!.hashCode) +
(withArchived == null ? 0 : withArchived!.hashCode) +
(withDeleted == null ? 0 : withDeleted!.hashCode) +
(withExif == null ? 0 : withExif!.hashCode);
@override
String toString() => 'SmartSearchDto[city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceId=$deviceId, isArchived=$isArchived, isEncoded=$isEncoded, isExternal=$isExternal, isFavorite=$isFavorite, isMotion=$isMotion, isOffline=$isOffline, isReadOnly=$isReadOnly, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, page=$page, query=$query, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
if (this.city != null) {
json[r'city'] = this.city;
} else {
// json[r'city'] = null;
}
if (this.country != null) {
json[r'country'] = this.country;
} else {
// json[r'country'] = null;
}
if (this.createdAfter != null) {
json[r'createdAfter'] = this.createdAfter!.toUtc().toIso8601String();
} else {
// json[r'createdAfter'] = null;
}
if (this.createdBefore != null) {
json[r'createdBefore'] = this.createdBefore!.toUtc().toIso8601String();
} else {
// json[r'createdBefore'] = null;
}
if (this.deviceId != null) {
json[r'deviceId'] = this.deviceId;
} else {
// json[r'deviceId'] = null;
}
if (this.isArchived != null) {
json[r'isArchived'] = this.isArchived;
} else {
// json[r'isArchived'] = null;
}
if (this.isEncoded != null) {
json[r'isEncoded'] = this.isEncoded;
} else {
// json[r'isEncoded'] = null;
}
if (this.isExternal != null) {
json[r'isExternal'] = this.isExternal;
} else {
// json[r'isExternal'] = null;
}
if (this.isFavorite != null) {
json[r'isFavorite'] = this.isFavorite;
} else {
// json[r'isFavorite'] = null;
}
if (this.isMotion != null) {
json[r'isMotion'] = this.isMotion;
} else {
// json[r'isMotion'] = null;
}
if (this.isOffline != null) {
json[r'isOffline'] = this.isOffline;
} else {
// json[r'isOffline'] = null;
}
if (this.isReadOnly != null) {
json[r'isReadOnly'] = this.isReadOnly;
} else {
// json[r'isReadOnly'] = null;
}
if (this.isVisible != null) {
json[r'isVisible'] = this.isVisible;
} else {
// json[r'isVisible'] = null;
}
if (this.lensModel != null) {
json[r'lensModel'] = this.lensModel;
} else {
// json[r'lensModel'] = null;
}
if (this.libraryId != null) {
json[r'libraryId'] = this.libraryId;
} else {
// json[r'libraryId'] = null;
}
if (this.make != null) {
json[r'make'] = this.make;
} else {
// json[r'make'] = null;
}
if (this.model != null) {
json[r'model'] = this.model;
} else {
// json[r'model'] = null;
}
if (this.page != null) {
json[r'page'] = this.page;
} else {
// json[r'page'] = null;
}
json[r'query'] = this.query;
if (this.size != null) {
json[r'size'] = this.size;
} else {
// json[r'size'] = null;
}
if (this.state != null) {
json[r'state'] = this.state;
} else {
// json[r'state'] = null;
}
if (this.takenAfter != null) {
json[r'takenAfter'] = this.takenAfter!.toUtc().toIso8601String();
} else {
// json[r'takenAfter'] = null;
}
if (this.takenBefore != null) {
json[r'takenBefore'] = this.takenBefore!.toUtc().toIso8601String();
} else {
// json[r'takenBefore'] = null;
}
if (this.trashedAfter != null) {
json[r'trashedAfter'] = this.trashedAfter!.toUtc().toIso8601String();
} else {
// json[r'trashedAfter'] = null;
}
if (this.trashedBefore != null) {
json[r'trashedBefore'] = this.trashedBefore!.toUtc().toIso8601String();
} else {
// json[r'trashedBefore'] = null;
}
if (this.type != null) {
json[r'type'] = this.type;
} else {
// json[r'type'] = null;
}
if (this.updatedAfter != null) {
json[r'updatedAfter'] = this.updatedAfter!.toUtc().toIso8601String();
} else {
// json[r'updatedAfter'] = null;
}
if (this.updatedBefore != null) {
json[r'updatedBefore'] = this.updatedBefore!.toUtc().toIso8601String();
} else {
// json[r'updatedBefore'] = null;
}
if (this.withArchived != null) {
json[r'withArchived'] = this.withArchived;
} else {
// json[r'withArchived'] = null;
}
if (this.withDeleted != null) {
json[r'withDeleted'] = this.withDeleted;
} else {
// json[r'withDeleted'] = null;
}
if (this.withExif != null) {
json[r'withExif'] = this.withExif;
} else {
// json[r'withExif'] = null;
}
return json;
}
/// Returns a new [SmartSearchDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static SmartSearchDto? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return SmartSearchDto(
city: mapValueOfType<String>(json, r'city'),
country: mapValueOfType<String>(json, r'country'),
createdAfter: mapDateTime(json, r'createdAfter', r''),
createdBefore: mapDateTime(json, r'createdBefore', r''),
deviceId: mapValueOfType<String>(json, r'deviceId'),
isArchived: mapValueOfType<bool>(json, r'isArchived'),
isEncoded: mapValueOfType<bool>(json, r'isEncoded'),
isExternal: mapValueOfType<bool>(json, r'isExternal'),
isFavorite: mapValueOfType<bool>(json, r'isFavorite'),
isMotion: mapValueOfType<bool>(json, r'isMotion'),
isOffline: mapValueOfType<bool>(json, r'isOffline'),
isReadOnly: mapValueOfType<bool>(json, r'isReadOnly'),
isVisible: mapValueOfType<bool>(json, r'isVisible'),
lensModel: mapValueOfType<String>(json, r'lensModel'),
libraryId: mapValueOfType<String>(json, r'libraryId'),
make: mapValueOfType<String>(json, r'make'),
model: mapValueOfType<String>(json, r'model'),
page: num.parse('${json[r'page']}'),
query: mapValueOfType<String>(json, r'query')!,
size: num.parse('${json[r'size']}'),
state: mapValueOfType<String>(json, r'state'),
takenAfter: mapDateTime(json, r'takenAfter', r''),
takenBefore: mapDateTime(json, r'takenBefore', r''),
trashedAfter: mapDateTime(json, r'trashedAfter', r''),
trashedBefore: mapDateTime(json, r'trashedBefore', r''),
type: AssetTypeEnum.fromJson(json[r'type']),
updatedAfter: mapDateTime(json, r'updatedAfter', r''),
updatedBefore: mapDateTime(json, r'updatedBefore', r''),
withArchived: mapValueOfType<bool>(json, r'withArchived'),
withDeleted: mapValueOfType<bool>(json, r'withDeleted'),
withExif: mapValueOfType<bool>(json, r'withExif'),
);
}
return null;
}
static List<SmartSearchDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <SmartSearchDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = SmartSearchDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, SmartSearchDto> mapFromJson(dynamic json) {
final map = <String, SmartSearchDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = SmartSearchDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of SmartSearchDto-objects as value to a dart map
static Map<String, List<SmartSearchDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<SmartSearchDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = SmartSearchDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'query',
};
}
+2 -2
View File
@@ -80,7 +80,7 @@ void main() {
// TODO
});
//Future<List<MapMarkerResponseDto>> getMapMarkers({ DateTime fileCreatedAfter, DateTime fileCreatedBefore, bool isArchived, bool isFavorite, bool withPartners }) async
//Future<List<MapMarkerResponseDto>> getMapMarkers({ DateTime fileCreatedAfter, DateTime fileCreatedBefore, bool isArchived, bool isFavorite }) async
test('test getMapMarkers', () async {
// TODO
});
@@ -110,7 +110,7 @@ void main() {
// TODO
});
//Future<List<AssetResponseDto>> searchAssets({ String checksum, String city, String country, DateTime createdAfter, DateTime createdBefore, String deviceAssetId, String deviceId, String encodedVideoPath, String id, bool isArchived, bool isEncoded, bool isExternal, bool isFavorite, bool isMotion, bool isNotInAlbum, bool isOffline, bool isReadOnly, bool isVisible, String lensModel, String libraryId, String make, String model, AssetOrder order, String originalFileName, String originalPath, num page, List<String> personIds, String resizePath, num size, String state, DateTime takenAfter, DateTime takenBefore, DateTime trashedAfter, DateTime trashedBefore, AssetTypeEnum type, DateTime updatedAfter, DateTime updatedBefore, String webpPath, bool withArchived, bool withDeleted, bool withExif, bool withPeople, bool withStacked }) async
//Future<List<AssetResponseDto>> searchAssets({ String checksum, String city, String country, DateTime createdAfter, DateTime createdBefore, String deviceAssetId, String deviceId, String encodedVideoPath, String id, bool isArchived, bool isEncoded, bool isExternal, bool isFavorite, bool isMotion, bool isOffline, bool isReadOnly, bool isVisible, String lensModel, String libraryId, String make, String model, AssetOrder order, String originalFileName, String originalPath, num page, String resizePath, num size, String state, DateTime takenAfter, DateTime takenBefore, DateTime trashedAfter, DateTime trashedBefore, AssetTypeEnum type, DateTime updatedAfter, DateTime updatedBefore, String webpPath, bool withArchived, bool withDeleted, bool withExif, bool withPeople, bool withStacked }) async
test('test searchAssets', () async {
// TODO
});
-237
View File
@@ -1,237 +0,0 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
import 'package:openapi/api.dart';
import 'package:test/test.dart';
// tests for MetadataSearchDto
void main() {
// final instance = MetadataSearchDto();
group('test MetadataSearchDto', () {
// String checksum
test('to test the property `checksum`', () async {
// TODO
});
// String city
test('to test the property `city`', () async {
// TODO
});
// String country
test('to test the property `country`', () async {
// TODO
});
// DateTime createdAfter
test('to test the property `createdAfter`', () async {
// TODO
});
// DateTime createdBefore
test('to test the property `createdBefore`', () async {
// TODO
});
// String deviceAssetId
test('to test the property `deviceAssetId`', () async {
// TODO
});
// String deviceId
test('to test the property `deviceId`', () async {
// TODO
});
// String encodedVideoPath
test('to test the property `encodedVideoPath`', () async {
// TODO
});
// String id
test('to test the property `id`', () async {
// TODO
});
// bool isArchived
test('to test the property `isArchived`', () async {
// TODO
});
// bool isEncoded
test('to test the property `isEncoded`', () async {
// TODO
});
// bool isExternal
test('to test the property `isExternal`', () async {
// TODO
});
// bool isFavorite
test('to test the property `isFavorite`', () async {
// TODO
});
// bool isMotion
test('to test the property `isMotion`', () async {
// TODO
});
// bool isNotInAlbum
test('to test the property `isNotInAlbum`', () async {
// TODO
});
// bool isOffline
test('to test the property `isOffline`', () async {
// TODO
});
// bool isReadOnly
test('to test the property `isReadOnly`', () async {
// TODO
});
// bool isVisible
test('to test the property `isVisible`', () async {
// TODO
});
// String lensModel
test('to test the property `lensModel`', () async {
// TODO
});
// String libraryId
test('to test the property `libraryId`', () async {
// TODO
});
// String make
test('to test the property `make`', () async {
// TODO
});
// String model
test('to test the property `model`', () async {
// TODO
});
// AssetOrder order
test('to test the property `order`', () async {
// TODO
});
// String originalFileName
test('to test the property `originalFileName`', () async {
// TODO
});
// String originalPath
test('to test the property `originalPath`', () async {
// TODO
});
// num page
test('to test the property `page`', () async {
// TODO
});
// List<String> personIds (default value: const [])
test('to test the property `personIds`', () async {
// TODO
});
// String resizePath
test('to test the property `resizePath`', () async {
// TODO
});
// num size
test('to test the property `size`', () async {
// TODO
});
// String state
test('to test the property `state`', () async {
// TODO
});
// DateTime takenAfter
test('to test the property `takenAfter`', () async {
// TODO
});
// DateTime takenBefore
test('to test the property `takenBefore`', () async {
// TODO
});
// DateTime trashedAfter
test('to test the property `trashedAfter`', () async {
// TODO
});
// DateTime trashedBefore
test('to test the property `trashedBefore`', () async {
// TODO
});
// AssetTypeEnum type
test('to test the property `type`', () async {
// TODO
});
// DateTime updatedAfter
test('to test the property `updatedAfter`', () async {
// TODO
});
// DateTime updatedBefore
test('to test the property `updatedBefore`', () async {
// TODO
});
// String webpPath
test('to test the property `webpPath`', () async {
// TODO
});
// bool withArchived
test('to test the property `withArchived`', () async {
// TODO
});
// bool withDeleted
test('to test the property `withDeleted`', () async {
// TODO
});
// bool withExif
test('to test the property `withExif`', () async {
// TODO
});
// bool withPeople
test('to test the property `withPeople`', () async {
// TODO
});
// bool withStacked
test('to test the property `withStacked`', () async {
// TODO
});
});
}
+2 -2
View File
@@ -32,7 +32,7 @@ void main() {
// TODO
});
//Future<SearchResponseDto> searchMetadata(MetadataSearchDto metadataSearchDto) async
//Future<SearchResponseDto> searchMetadata({ String checksum, String city, String country, DateTime createdAfter, DateTime createdBefore, String deviceAssetId, String deviceId, String encodedVideoPath, String id, bool isArchived, bool isEncoded, bool isExternal, bool isFavorite, bool isMotion, bool isOffline, bool isReadOnly, bool isVisible, String lensModel, String libraryId, String make, String model, AssetOrder order, String originalFileName, String originalPath, num page, String resizePath, num size, String state, DateTime takenAfter, DateTime takenBefore, DateTime trashedAfter, DateTime trashedBefore, AssetTypeEnum type, DateTime updatedAfter, DateTime updatedBefore, String webpPath, bool withArchived, bool withDeleted, bool withExif, bool withPeople, bool withStacked }) async
test('test searchMetadata', () async {
// TODO
});
@@ -42,7 +42,7 @@ void main() {
// TODO
});
//Future<SearchResponseDto> searchSmart(SmartSearchDto smartSearchDto) async
//Future<SearchResponseDto> searchSmart(String query, { String city, String country, DateTime createdAfter, DateTime createdBefore, String deviceId, bool isArchived, bool isEncoded, bool isExternal, bool isFavorite, bool isMotion, bool isOffline, bool isReadOnly, bool isVisible, String lensModel, String libraryId, String make, String model, num page, num size, String state, DateTime takenAfter, DateTime takenBefore, DateTime trashedAfter, DateTime trashedBefore, AssetTypeEnum type, DateTime updatedAfter, DateTime updatedBefore, bool withArchived, bool withDeleted, bool withExif }) async
test('test searchSmart', () async {
// TODO
});
-177
View File
@@ -1,177 +0,0 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.12
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
import 'package:openapi/api.dart';
import 'package:test/test.dart';
// tests for SmartSearchDto
void main() {
// final instance = SmartSearchDto();
group('test SmartSearchDto', () {
// String city
test('to test the property `city`', () async {
// TODO
});
// String country
test('to test the property `country`', () async {
// TODO
});
// DateTime createdAfter
test('to test the property `createdAfter`', () async {
// TODO
});
// DateTime createdBefore
test('to test the property `createdBefore`', () async {
// TODO
});
// String deviceId
test('to test the property `deviceId`', () async {
// TODO
});
// bool isArchived
test('to test the property `isArchived`', () async {
// TODO
});
// bool isEncoded
test('to test the property `isEncoded`', () async {
// TODO
});
// bool isExternal
test('to test the property `isExternal`', () async {
// TODO
});
// bool isFavorite
test('to test the property `isFavorite`', () async {
// TODO
});
// bool isMotion
test('to test the property `isMotion`', () async {
// TODO
});
// bool isOffline
test('to test the property `isOffline`', () async {
// TODO
});
// bool isReadOnly
test('to test the property `isReadOnly`', () async {
// TODO
});
// bool isVisible
test('to test the property `isVisible`', () async {
// TODO
});
// String lensModel
test('to test the property `lensModel`', () async {
// TODO
});
// String libraryId
test('to test the property `libraryId`', () async {
// TODO
});
// String make
test('to test the property `make`', () async {
// TODO
});
// String model
test('to test the property `model`', () async {
// TODO
});
// num page
test('to test the property `page`', () async {
// TODO
});
// String query
test('to test the property `query`', () async {
// TODO
});
// num size
test('to test the property `size`', () async {
// TODO
});
// String state
test('to test the property `state`', () async {
// TODO
});
// DateTime takenAfter
test('to test the property `takenAfter`', () async {
// TODO
});
// DateTime takenBefore
test('to test the property `takenBefore`', () async {
// TODO
});
// DateTime trashedAfter
test('to test the property `trashedAfter`', () async {
// TODO
});
// DateTime trashedBefore
test('to test the property `trashedBefore`', () async {
// TODO
});
// AssetTypeEnum type
test('to test the property `type`', () async {
// TODO
});
// DateTime updatedAfter
test('to test the property `updatedAfter`', () async {
// TODO
});
// DateTime updatedBefore
test('to test the property `updatedBefore`', () async {
// TODO
});
// bool withArchived
test('to test the property `withArchived`', () async {
// TODO
});
// bool withDeleted
test('to test the property `withDeleted`', () async {
// TODO
});
// bool withExif
test('to test the property `withExif`', () async {
// TODO
});
});
}
+1 -1
View File
@@ -19,7 +19,7 @@ function dart {
function typescript {
rm -rf ./typescript-sdk/client
npx --yes @openapitools/openapi-generator-cli generate -g typescript-axios -i ./immich-openapi-specs.json -o ./typescript-sdk/axios-client --additional-properties=useSingleRequestParameter=true,supportsES6=true
npx --yes oazapfts --optimistic --argumentStyle=object --useEnumType immich-openapi-specs.json typescript-sdk/fetch-client.ts
npx --yes oazapfts --optimistic --argumentStyle=object immich-openapi-specs.json typescript-sdk/fetch-client.ts
npm --prefix typescript-sdk ci && npm --prefix typescript-sdk run build
}
+599 -306
View File
@@ -1397,14 +1397,6 @@
"schema": {
"type": "boolean"
}
},
{
"name": "withPartners",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
}
],
"responses": {
@@ -2256,14 +2248,6 @@
"type": "boolean"
}
},
{
"name": "isNotInAlbum",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "isOffline",
"required": false,
@@ -2353,17 +2337,6 @@
"type": "number"
}
},
{
"name": "personIds",
"required": false,
"in": "query",
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
},
{
"name": "resizePath",
"required": false,
@@ -4545,21 +4518,350 @@
}
},
"/search/metadata": {
"post": {
"get": {
"operationId": "searchMetadata",
"parameters": [],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/MetadataSearchDto"
}
"parameters": [
{
"name": "checksum",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
"required": true
},
{
"name": "city",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "country",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "createdAfter",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "createdBefore",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "deviceAssetId",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "deviceId",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "encodedVideoPath",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "id",
"required": false,
"in": "query",
"schema": {
"format": "uuid",
"type": "string"
}
},
{
"name": "isArchived",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "isEncoded",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "isExternal",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "isFavorite",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "isMotion",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "isOffline",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "isReadOnly",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "isVisible",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "lensModel",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "libraryId",
"required": false,
"in": "query",
"schema": {
"format": "uuid",
"type": "string"
}
},
{
"name": "make",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "model",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "order",
"required": false,
"in": "query",
"schema": {
"$ref": "#/components/schemas/AssetOrder"
}
},
{
"name": "originalFileName",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "originalPath",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "page",
"required": false,
"in": "query",
"schema": {
"type": "number"
}
},
{
"name": "resizePath",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "size",
"required": false,
"in": "query",
"schema": {
"type": "number"
}
},
{
"name": "state",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "takenAfter",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "takenBefore",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "trashedAfter",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "trashedBefore",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "type",
"required": false,
"in": "query",
"schema": {
"$ref": "#/components/schemas/AssetTypeEnum"
}
},
{
"name": "updatedAfter",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "updatedBefore",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "webpPath",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "withArchived",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "withDeleted",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "withExif",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "withPeople",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "withStacked",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
}
],
"responses": {
"201": {
"200": {
"content": {
"application/json": {
"schema": {
@@ -4639,21 +4941,269 @@
}
},
"/search/smart": {
"post": {
"get": {
"operationId": "searchSmart",
"parameters": [],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SmartSearchDto"
}
"parameters": [
{
"name": "city",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
"required": true
},
{
"name": "country",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "createdAfter",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "createdBefore",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "deviceId",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "isArchived",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "isEncoded",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "isExternal",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "isFavorite",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "isMotion",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "isOffline",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "isReadOnly",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "isVisible",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "lensModel",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "libraryId",
"required": false,
"in": "query",
"schema": {
"format": "uuid",
"type": "string"
}
},
{
"name": "make",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "model",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "page",
"required": false,
"in": "query",
"schema": {
"type": "number"
}
},
{
"name": "query",
"required": true,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "size",
"required": false,
"in": "query",
"schema": {
"type": "number"
}
},
{
"name": "state",
"required": false,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "takenAfter",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "takenBefore",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "trashedAfter",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "trashedBefore",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "type",
"required": false,
"in": "query",
"schema": {
"$ref": "#/components/schemas/AssetTypeEnum"
}
},
{
"name": "updatedAfter",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "updatedBefore",
"required": false,
"in": "query",
"schema": {
"format": "date-time",
"type": "string"
}
},
{
"name": "withArchived",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "withDeleted",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
},
{
"name": "withExif",
"required": false,
"in": "query",
"schema": {
"type": "boolean"
}
}
],
"responses": {
"201": {
"200": {
"content": {
"application/json": {
"schema": {
@@ -8247,153 +8797,6 @@
],
"type": "object"
},
"MetadataSearchDto": {
"properties": {
"checksum": {
"type": "string"
},
"city": {
"type": "string"
},
"country": {
"type": "string"
},
"createdAfter": {
"format": "date-time",
"type": "string"
},
"createdBefore": {
"format": "date-time",
"type": "string"
},
"deviceAssetId": {
"type": "string"
},
"deviceId": {
"type": "string"
},
"encodedVideoPath": {
"type": "string"
},
"id": {
"format": "uuid",
"type": "string"
},
"isArchived": {
"type": "boolean"
},
"isEncoded": {
"type": "boolean"
},
"isExternal": {
"type": "boolean"
},
"isFavorite": {
"type": "boolean"
},
"isMotion": {
"type": "boolean"
},
"isNotInAlbum": {
"type": "boolean"
},
"isOffline": {
"type": "boolean"
},
"isReadOnly": {
"type": "boolean"
},
"isVisible": {
"type": "boolean"
},
"lensModel": {
"type": "string"
},
"libraryId": {
"format": "uuid",
"type": "string"
},
"make": {
"type": "string"
},
"model": {
"type": "string"
},
"order": {
"$ref": "#/components/schemas/AssetOrder"
},
"originalFileName": {
"type": "string"
},
"originalPath": {
"type": "string"
},
"page": {
"type": "number"
},
"personIds": {
"items": {
"type": "string"
},
"type": "array"
},
"resizePath": {
"type": "string"
},
"size": {
"type": "number"
},
"state": {
"type": "string"
},
"takenAfter": {
"format": "date-time",
"type": "string"
},
"takenBefore": {
"format": "date-time",
"type": "string"
},
"trashedAfter": {
"format": "date-time",
"type": "string"
},
"trashedBefore": {
"format": "date-time",
"type": "string"
},
"type": {
"$ref": "#/components/schemas/AssetTypeEnum"
},
"updatedAfter": {
"format": "date-time",
"type": "string"
},
"updatedBefore": {
"format": "date-time",
"type": "string"
},
"webpPath": {
"type": "string"
},
"withArchived": {
"type": "boolean"
},
"withDeleted": {
"type": "boolean"
},
"withExif": {
"type": "boolean"
},
"withPeople": {
"type": "boolean"
},
"withStacked": {
"type": "boolean"
}
},
"type": "object"
},
"ModelType": {
"enum": [
"facial-recognition",
@@ -9349,116 +9752,6 @@
},
"type": "object"
},
"SmartSearchDto": {
"properties": {
"city": {
"type": "string"
},
"country": {
"type": "string"
},
"createdAfter": {
"format": "date-time",
"type": "string"
},
"createdBefore": {
"format": "date-time",
"type": "string"
},
"deviceId": {
"type": "string"
},
"isArchived": {
"type": "boolean"
},
"isEncoded": {
"type": "boolean"
},
"isExternal": {
"type": "boolean"
},
"isFavorite": {
"type": "boolean"
},
"isMotion": {
"type": "boolean"
},
"isOffline": {
"type": "boolean"
},
"isReadOnly": {
"type": "boolean"
},
"isVisible": {
"type": "boolean"
},
"lensModel": {
"type": "string"
},
"libraryId": {
"format": "uuid",
"type": "string"
},
"make": {
"type": "string"
},
"model": {
"type": "string"
},
"page": {
"type": "number"
},
"query": {
"type": "string"
},
"size": {
"type": "number"
},
"state": {
"type": "string"
},
"takenAfter": {
"format": "date-time",
"type": "string"
},
"takenBefore": {
"format": "date-time",
"type": "string"
},
"trashedAfter": {
"format": "date-time",
"type": "string"
},
"trashedBefore": {
"format": "date-time",
"type": "string"
},
"type": {
"$ref": "#/components/schemas/AssetTypeEnum"
},
"updatedAfter": {
"format": "date-time",
"type": "string"
},
"updatedBefore": {
"format": "date-time",
"type": "string"
},
"withArchived": {
"type": "boolean"
},
"withDeleted": {
"type": "boolean"
},
"withExif": {
"type": "boolean"
}
},
"required": [
"query"
],
"type": "object"
},
"SystemConfigDto": {
"properties": {
"ffmpeg": {
File diff suppressed because it is too large Load Diff
+195 -295
View File
@@ -14,6 +14,9 @@ const oazapfts = Oazapfts.runtime(defaults);
export const servers = {
server1: "/api"
};
export type ReactionLevel = "album" | "asset";
export type ReactionType = "comment" | "like";
export type UserAvatarColor = "primary" | "pink" | "red" | "yellow" | "blue" | "green" | "purple" | "orange" | "gray" | "amber";
export type UserDto = {
avatarColor: UserAvatarColor;
email: string;
@@ -26,7 +29,7 @@ export type ActivityResponseDto = {
comment?: string | null;
createdAt: string;
id: string;
"type": Type;
"type": "comment" | "like";
user: UserDto;
};
export type ActivityCreateDto = {
@@ -100,12 +103,14 @@ export type SmartInfoResponseDto = {
objects?: string[] | null;
tags?: string[] | null;
};
export type TagTypeEnum = "OBJECT" | "FACE" | "CUSTOM";
export type TagResponseDto = {
id: string;
name: string;
"type": TagTypeEnum;
userId: string;
};
export type AssetTypeEnum = "IMAGE" | "VIDEO" | "AUDIO" | "OTHER";
export type AssetResponseDto = {
/** base64 encoded sha1 hash */
checksum: string;
@@ -181,7 +186,7 @@ export type BulkIdsDto = {
ids: string[];
};
export type BulkIdResponseDto = {
error?: Error;
error?: "duplicate" | "no_permission" | "not_found" | "unknown";
id: string;
success: boolean;
};
@@ -227,10 +232,10 @@ export type AssetBulkUploadCheckDto = {
assets: AssetBulkUploadCheckItem[];
};
export type AssetBulkUploadCheckResult = {
action: Action;
action: "accept" | "reject";
assetId?: string;
id: string;
reason?: Reason;
reason?: "duplicate" | "unsupported-format";
};
export type AssetBulkUploadCheckResponseDto = {
results: AssetBulkUploadCheckResult[];
@@ -256,6 +261,7 @@ export type CheckExistingAssetsDto = {
export type CheckExistingAssetsResponseDto = {
existingIds: string[];
};
export type AssetJobName = "regenerate-thumbnail" | "refresh-metadata" | "transcode-video";
export type AssetJobsDto = {
assetIds: string[];
name: AssetJobName;
@@ -278,6 +284,8 @@ export type AssetStatsResponseDto = {
total: number;
videos: number;
};
export type ThumbnailFormat = "JPEG" | "WEBP";
export type TimeBucketSize = "DAY" | "MONTH";
export type TimeBucketResponseDto = {
count: number;
timeBucket: string;
@@ -311,10 +319,14 @@ export type UpdateAssetDto = {
latitude?: number;
longitude?: number;
};
export type AssetOrder = "asc" | "desc";
export type EntityType = "ASSET" | "ALBUM";
export type AuditDeletesResponseDto = {
ids: string[];
needsFullSync: boolean;
};
export type PathEntityType = "asset" | "person" | "user";
export type PathType = "original" | "jpeg_thumbnail" | "webp_thumbnail" | "encoded_video" | "sidecar" | "face" | "profile";
export type FileReportItemDto = {
checksum?: string;
entityId: string;
@@ -440,10 +452,13 @@ export type AllJobStatusResponseDto = {
thumbnailGeneration: JobStatusDto;
videoConversion: JobStatusDto;
};
export type JobName = "thumbnailGeneration" | "metadataExtraction" | "videoConversion" | "faceDetection" | "facialRecognition" | "smartSearch" | "backgroundTask" | "storageTemplateMigration" | "migration" | "search" | "sidecar" | "library";
export type JobCommand = "start" | "pause" | "resume" | "empty" | "clear-failed";
export type JobCommandDto = {
command: JobCommand;
force: boolean;
};
export type LibraryType = "UPLOAD" | "EXTERNAL";
export type LibraryResponseDto = {
assetCount: number;
createdAt: string;
@@ -588,84 +603,7 @@ export type SearchExploreResponseDto = {
fieldName: string;
items: SearchExploreItem[];
};
export type MetadataSearchDto = {
checksum?: string;
city?: string;
country?: string;
createdAfter?: string;
createdBefore?: string;
deviceAssetId?: string;
deviceId?: string;
encodedVideoPath?: string;
id?: string;
isArchived?: boolean;
isEncoded?: boolean;
isExternal?: boolean;
isFavorite?: boolean;
isMotion?: boolean;
isNotInAlbum?: boolean;
isOffline?: boolean;
isReadOnly?: boolean;
isVisible?: boolean;
lensModel?: string;
libraryId?: string;
make?: string;
model?: string;
order?: AssetOrder;
originalFileName?: string;
originalPath?: string;
page?: number;
personIds?: string[];
resizePath?: string;
size?: number;
state?: string;
takenAfter?: string;
takenBefore?: string;
trashedAfter?: string;
trashedBefore?: string;
"type"?: AssetTypeEnum;
updatedAfter?: string;
updatedBefore?: string;
webpPath?: string;
withArchived?: boolean;
withDeleted?: boolean;
withExif?: boolean;
withPeople?: boolean;
withStacked?: boolean;
};
export type SmartSearchDto = {
city?: string;
country?: string;
createdAfter?: string;
createdBefore?: string;
deviceId?: string;
isArchived?: boolean;
isEncoded?: boolean;
isExternal?: boolean;
isFavorite?: boolean;
isMotion?: boolean;
isOffline?: boolean;
isReadOnly?: boolean;
isVisible?: boolean;
lensModel?: string;
libraryId?: string;
make?: string;
model?: string;
page?: number;
query: string;
size?: number;
state?: string;
takenAfter?: string;
takenBefore?: string;
trashedAfter?: string;
trashedBefore?: string;
"type"?: AssetTypeEnum;
updatedAfter?: string;
updatedBefore?: string;
withArchived?: boolean;
withDeleted?: boolean;
withExif?: boolean;
};
export type SearchSuggestionType = "country" | "state" | "city" | "camera-make" | "camera-model";
export type ServerInfoResponseDto = {
diskAvailable: string;
diskAvailableRaw: number;
@@ -727,6 +665,7 @@ export type ServerVersionResponseDto = {
minor: number;
patch: number;
};
export type SharedLinkType = "ALBUM" | "INDIVIDUAL";
export type SharedLinkResponseDto = {
album?: AlbumResponseDto;
allowDownload: boolean;
@@ -768,15 +707,21 @@ export type SharedLinkEditDto = {
};
export type AssetIdsResponseDto = {
assetId: string;
error?: Error2;
error?: "duplicate" | "no_permission" | "not_found";
success: boolean;
};
export type TranscodeHwAccel = "nvenc" | "qsv" | "vaapi" | "rkmpp" | "disabled";
export type AudioCodec = "mp3" | "aac" | "libopus";
export type VideoCodec = "h264" | "hevc" | "vp9";
export type CqMode = "auto" | "cqp" | "icq";
export type ToneMapping = "hable" | "mobius" | "reinhard" | "disabled";
export type TranscodePolicy = "all" | "optimal" | "bitrate" | "required" | "disabled";
export type SystemConfigFFmpegDto = {
accel: TranscodeHWAccel;
accel: TranscodeHwAccel;
acceptedAudioCodecs: AudioCodec[];
acceptedVideoCodecs: VideoCodec[];
bframes: number;
cqMode: CQMode;
cqMode: CqMode;
crf: number;
gopSize: number;
maxBitrate: string;
@@ -821,13 +766,16 @@ export type SystemConfigLibraryDto = {
scan: SystemConfigLibraryScanDto;
watch: SystemConfigLibraryWatchDto;
};
export type LogLevel = "verbose" | "debug" | "log" | "warn" | "error" | "fatal";
export type SystemConfigLoggingDto = {
enabled: boolean;
level: LogLevel;
};
export type ClipMode = "vision" | "text";
export type ModelType = "facial-recognition" | "clip";
export type ClipConfig = {
enabled: boolean;
mode?: CLIPMode;
mode?: ClipMode;
modelName: string;
modelType?: ModelType;
};
@@ -885,6 +833,7 @@ export type SystemConfigStorageTemplateDto = {
export type SystemConfigThemeDto = {
customCss: string;
};
export type Colorspace = "srgb" | "p3";
export type SystemConfigThumbnailDto = {
colorspace: Colorspace;
jpegSize: number;
@@ -912,6 +861,7 @@ export type SystemConfigDto = {
thumbnail: SystemConfigThumbnailDto;
trash: SystemConfigTrashDto;
};
export type MapTheme = "light" | "dark";
export type SystemConfigTemplateStorageOptionDto = {
dayOptions: string[];
hourOptions: string[];
@@ -1318,12 +1268,11 @@ export function runAssetJobs({ assetJobsDto }: {
body: assetJobsDto
})));
}
export function getMapMarkers({ fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite, withPartners }: {
export function getMapMarkers({ fileCreatedAfter, fileCreatedBefore, isArchived, isFavorite }: {
fileCreatedAfter?: string;
fileCreatedBefore?: string;
isArchived?: boolean;
isFavorite?: boolean;
withPartners?: boolean;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
@@ -1332,8 +1281,7 @@ export function getMapMarkers({ fileCreatedAfter, fileCreatedBefore, isArchived,
fileCreatedAfter,
fileCreatedBefore,
isArchived,
isFavorite,
withPartners
isFavorite
}))}`, {
...opts
}));
@@ -1515,7 +1463,7 @@ export function updateAsset({ id, updateAssetDto }: {
body: updateAssetDto
})));
}
export function searchAssets({ checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isNotInAlbum, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, personIds, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked }: {
export function searchAssets({ checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked }: {
checksum?: string;
city?: string;
country?: string;
@@ -1530,7 +1478,6 @@ export function searchAssets({ checksum, city, country, createdAfter, createdBef
isExternal?: boolean;
isFavorite?: boolean;
isMotion?: boolean;
isNotInAlbum?: boolean;
isOffline?: boolean;
isReadOnly?: boolean;
isVisible?: boolean;
@@ -1542,7 +1489,6 @@ export function searchAssets({ checksum, city, country, createdAfter, createdBef
originalFileName?: string;
originalPath?: string;
page?: number;
personIds?: string[];
resizePath?: string;
size?: number;
state?: string;
@@ -1578,7 +1524,6 @@ export function searchAssets({ checksum, city, country, createdAfter, createdBef
isExternal,
isFavorite,
isMotion,
isNotInAlbum,
isOffline,
isReadOnly,
isVisible,
@@ -1590,7 +1535,6 @@ export function searchAssets({ checksum, city, country, createdAfter, createdBef
originalFileName,
originalPath,
page,
personIds,
resizePath,
size,
state,
@@ -2145,17 +2089,97 @@ export function getExploreData(opts?: Oazapfts.RequestOpts) {
...opts
}));
}
export function searchMetadata({ metadataSearchDto }: {
metadataSearchDto: MetadataSearchDto;
export function searchMetadata({ checksum, city, country, createdAfter, createdBefore, deviceAssetId, deviceId, encodedVideoPath, id, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, order, originalFileName, originalPath, page, resizePath, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, webpPath, withArchived, withDeleted, withExif, withPeople, withStacked }: {
checksum?: string;
city?: string;
country?: string;
createdAfter?: string;
createdBefore?: string;
deviceAssetId?: string;
deviceId?: string;
encodedVideoPath?: string;
id?: string;
isArchived?: boolean;
isEncoded?: boolean;
isExternal?: boolean;
isFavorite?: boolean;
isMotion?: boolean;
isOffline?: boolean;
isReadOnly?: boolean;
isVisible?: boolean;
lensModel?: string;
libraryId?: string;
make?: string;
model?: string;
order?: AssetOrder;
originalFileName?: string;
originalPath?: string;
page?: number;
resizePath?: string;
size?: number;
state?: string;
takenAfter?: string;
takenBefore?: string;
trashedAfter?: string;
trashedBefore?: string;
$type?: AssetTypeEnum;
updatedAfter?: string;
updatedBefore?: string;
webpPath?: string;
withArchived?: boolean;
withDeleted?: boolean;
withExif?: boolean;
withPeople?: boolean;
withStacked?: boolean;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 201;
status: 200;
data: SearchResponseDto;
}>("/search/metadata", oazapfts.json({
...opts,
method: "POST",
body: metadataSearchDto
})));
}>(`/search/metadata${QS.query(QS.explode({
checksum,
city,
country,
createdAfter,
createdBefore,
deviceAssetId,
deviceId,
encodedVideoPath,
id,
isArchived,
isEncoded,
isExternal,
isFavorite,
isMotion,
isOffline,
isReadOnly,
isVisible,
lensModel,
libraryId,
make,
model,
order,
originalFileName,
originalPath,
page,
resizePath,
size,
state,
takenAfter,
takenBefore,
trashedAfter,
trashedBefore,
"type": $type,
updatedAfter,
updatedBefore,
webpPath,
withArchived,
withDeleted,
withExif,
withPeople,
withStacked
}))}`, {
...opts
}));
}
export function searchPerson({ name, withHidden }: {
name: string;
@@ -2171,17 +2195,77 @@ export function searchPerson({ name, withHidden }: {
...opts
}));
}
export function searchSmart({ smartSearchDto }: {
smartSearchDto: SmartSearchDto;
export function searchSmart({ city, country, createdAfter, createdBefore, deviceId, isArchived, isEncoded, isExternal, isFavorite, isMotion, isOffline, isReadOnly, isVisible, lensModel, libraryId, make, model, page, query, size, state, takenAfter, takenBefore, trashedAfter, trashedBefore, $type, updatedAfter, updatedBefore, withArchived, withDeleted, withExif }: {
city?: string;
country?: string;
createdAfter?: string;
createdBefore?: string;
deviceId?: string;
isArchived?: boolean;
isEncoded?: boolean;
isExternal?: boolean;
isFavorite?: boolean;
isMotion?: boolean;
isOffline?: boolean;
isReadOnly?: boolean;
isVisible?: boolean;
lensModel?: string;
libraryId?: string;
make?: string;
model?: string;
page?: number;
query: string;
size?: number;
state?: string;
takenAfter?: string;
takenBefore?: string;
trashedAfter?: string;
trashedBefore?: string;
$type?: AssetTypeEnum;
updatedAfter?: string;
updatedBefore?: string;
withArchived?: boolean;
withDeleted?: boolean;
withExif?: boolean;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 201;
status: 200;
data: SearchResponseDto;
}>("/search/smart", oazapfts.json({
...opts,
method: "POST",
body: smartSearchDto
})));
}>(`/search/smart${QS.query(QS.explode({
city,
country,
createdAfter,
createdBefore,
deviceId,
isArchived,
isEncoded,
isExternal,
isFavorite,
isMotion,
isOffline,
isReadOnly,
isVisible,
lensModel,
libraryId,
make,
model,
page,
query,
size,
state,
takenAfter,
takenBefore,
trashedAfter,
trashedBefore,
"type": $type,
updatedAfter,
updatedBefore,
withArchived,
withDeleted,
withExif
}))}`, {
...opts
}));
}
export function getSearchSuggestions({ country, make, model, state, $type }: {
country?: string;
@@ -2632,187 +2716,3 @@ export function restoreUser({ id }: {
method: "POST"
}));
}
export enum ReactionLevel {
Album = "album",
Asset = "asset"
}
export enum ReactionType {
Comment = "comment",
Like = "like"
}
export enum Type {
Comment = "comment",
Like = "like"
}
export enum UserAvatarColor {
Primary = "primary",
Pink = "pink",
Red = "red",
Yellow = "yellow",
Blue = "blue",
Green = "green",
Purple = "purple",
Orange = "orange",
Gray = "gray",
Amber = "amber"
}
export enum TagTypeEnum {
Object = "OBJECT",
Face = "FACE",
Custom = "CUSTOM"
}
export enum AssetTypeEnum {
Image = "IMAGE",
Video = "VIDEO",
Audio = "AUDIO",
Other = "OTHER"
}
export enum Error {
Duplicate = "duplicate",
NoPermission = "no_permission",
NotFound = "not_found",
Unknown = "unknown"
}
export enum Action {
Accept = "accept",
Reject = "reject"
}
export enum Reason {
Duplicate = "duplicate",
UnsupportedFormat = "unsupported-format"
}
export enum AssetJobName {
RegenerateThumbnail = "regenerate-thumbnail",
RefreshMetadata = "refresh-metadata",
TranscodeVideo = "transcode-video"
}
export enum ThumbnailFormat {
Jpeg = "JPEG",
Webp = "WEBP"
}
export enum TimeBucketSize {
Day = "DAY",
Month = "MONTH"
}
export enum AssetOrder {
Asc = "asc",
Desc = "desc"
}
export enum EntityType {
Asset = "ASSET",
Album = "ALBUM"
}
export enum PathEntityType {
Asset = "asset",
Person = "person",
User = "user"
}
export enum PathType {
Original = "original",
JpegThumbnail = "jpeg_thumbnail",
WebpThumbnail = "webp_thumbnail",
EncodedVideo = "encoded_video",
Sidecar = "sidecar",
Face = "face",
Profile = "profile"
}
export enum JobName {
ThumbnailGeneration = "thumbnailGeneration",
MetadataExtraction = "metadataExtraction",
VideoConversion = "videoConversion",
FaceDetection = "faceDetection",
FacialRecognition = "facialRecognition",
SmartSearch = "smartSearch",
BackgroundTask = "backgroundTask",
StorageTemplateMigration = "storageTemplateMigration",
Migration = "migration",
Search = "search",
Sidecar = "sidecar",
Library = "library"
}
export enum JobCommand {
Start = "start",
Pause = "pause",
Resume = "resume",
Empty = "empty",
ClearFailed = "clear-failed"
}
export enum LibraryType {
Upload = "UPLOAD",
External = "EXTERNAL"
}
export enum SearchSuggestionType {
Country = "country",
State = "state",
City = "city",
CameraMake = "camera-make",
CameraModel = "camera-model"
}
export enum SharedLinkType {
Album = "ALBUM",
Individual = "INDIVIDUAL"
}
export enum Error2 {
Duplicate = "duplicate",
NoPermission = "no_permission",
NotFound = "not_found"
}
export enum TranscodeHWAccel {
Nvenc = "nvenc",
Qsv = "qsv",
Vaapi = "vaapi",
Rkmpp = "rkmpp",
Disabled = "disabled"
}
export enum AudioCodec {
Mp3 = "mp3",
Aac = "aac",
Libopus = "libopus"
}
export enum VideoCodec {
H264 = "h264",
Hevc = "hevc",
Vp9 = "vp9"
}
export enum CQMode {
Auto = "auto",
Cqp = "cqp",
Icq = "icq"
}
export enum ToneMapping {
Hable = "hable",
Mobius = "mobius",
Reinhard = "reinhard",
Disabled = "disabled"
}
export enum TranscodePolicy {
All = "all",
Optimal = "optimal",
Bitrate = "bitrate",
Required = "required",
Disabled = "disabled"
}
export enum LogLevel {
Verbose = "verbose",
Debug = "debug",
Log = "log",
Warn = "warn",
Error = "error",
Fatal = "fatal"
}
export enum CLIPMode {
Vision = "vision",
Text = "text"
}
export enum ModelType {
FacialRecognition = "facial-recognition",
Clip = "clip"
}
export enum Colorspace {
Srgb = "srgb",
P3 = "p3"
}
export enum MapTheme {
Light = "light",
Dark = "dark"
}
+3 -3
View File
@@ -29,9 +29,9 @@
"dev": true
},
"node_modules/@types/node": {
"version": "20.11.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.17.tgz",
"integrity": "sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw==",
"version": "20.11.16",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.16.tgz",
"integrity": "sha512-gKb0enTmRCzXSSUJDq6/sPcqrfCv2mkkG6Jt/clpn5eiCbKTY+SgZUxo+p8ZKMof5dCp9vHQUAB7wOUTod22wQ==",
"dev": true,
"dependencies": {
"undici-types": "~5.26.4"
+1 -1
View File
@@ -1,5 +1,5 @@
{
"include": ["fetch.ts", "fetch-client.ts"],
"include": ["fetch.ts"],
"compilerOptions": {
"target": "esnext",
"strict": true,
+1 -5
View File
@@ -1,4 +1,4 @@
import { AssetBulkDeleteDto, AssetResponseDto } from '@app/domain';
import { AssetResponseDto } from '@app/domain';
import { CreateAssetDto } from '@app/immich/api-v1/asset/dto/create-asset.dto';
import { AssetFileUploadResponseDto } from '@app/immich/api-v1/asset/response-dto/asset-file-upload-response.dto';
import { randomBytes } from 'node:crypto';
@@ -74,8 +74,4 @@ export const assetApi = {
expect(status).toBe(200);
return body;
},
delete: async (server: any, accessToken: string, dto: AssetBulkDeleteDto) => {
const { status } = await request(server).delete('/asset').set('Authorization', `Bearer ${accessToken}`).send(dto);
expect(status).toBe(204);
},
};
-2
View File
@@ -7,7 +7,6 @@ import { libraryApi } from './library-api';
import { partnerApi } from './partner-api';
import { serverInfoApi } from './server-info-api';
import { sharedLinkApi } from './shared-link-api';
import { trashApi } from './trash-api';
import { userApi } from './user-api';
export const api = {
@@ -18,7 +17,6 @@ export const api = {
libraryApi,
serverInfoApi,
sharedLinkApi,
trashApi,
albumApi,
userApi,
partnerApi,
-13
View File
@@ -1,13 +0,0 @@
import request from 'supertest';
import type { App } from 'supertest/types';
export const trashApi = {
async empty(server: App, accessToken: string) {
const { status } = await request(server).post('/trash/empty').set('Authorization', `Bearer ${accessToken}`);
expect(status).toBe(204);
},
async restore(server: App, accessToken: string) {
const { status } = await request(server).post('/trash/restore').set('Authorization', `Bearer ${accessToken}`);
expect(status).toBe(204);
},
};
-80
View File
@@ -1,80 +0,0 @@
import { LoginResponseDto } from '@app/domain';
import { api } from 'e2e/client';
import { readFile } from 'node:fs/promises';
import { basename, join } from 'node:path';
import type { App } from 'supertest/types';
import { IMMICH_TEST_ASSET_PATH, testApp } from '../../../src/test-utils/utils';
const assetFilePath = join(IMMICH_TEST_ASSET_PATH, 'formats/png/density_plot.png');
describe(`Trash (e2e)`, () => {
let server: App;
let admin: LoginResponseDto;
beforeAll(async () => {
const app = await testApp.create();
server = app.getHttpServer();
});
beforeEach(async () => {
await testApp.reset();
await api.authApi.adminSignUp(server);
admin = await api.authApi.adminLogin(server);
});
afterAll(async () => {
await testApp.teardown();
});
it('should move an asset to trash', async () => {
const content = await readFile(assetFilePath);
const { id: assetId } = await api.assetApi.upload(server, admin.accessToken, 'test-device-id', {
content,
filename: basename(assetFilePath),
});
const uploadedAsset = await api.assetApi.get(server, admin.accessToken, assetId);
expect(uploadedAsset.isTrashed).toBe(false);
await api.assetApi.delete(server, admin.accessToken, { ids: [assetId] });
const deletedAsset = await api.assetApi.get(server, admin.accessToken, assetId);
expect(deletedAsset.isTrashed).toBe(true);
});
it('should delete all trashed assets', async () => {
const content = await readFile(assetFilePath);
const { id: assetId } = await api.assetApi.upload(server, admin.accessToken, 'test-device-id', {
content,
filename: basename(assetFilePath),
});
await api.assetApi.delete(server, admin.accessToken, { ids: [assetId] });
const assetsBeforeEmpty = await api.assetApi.getAllAssets(server, admin.accessToken);
expect(assetsBeforeEmpty.length).toBe(1);
await api.trashApi.empty(server, admin.accessToken);
const assetsAfterEmpty = await api.assetApi.getAllAssets(server, admin.accessToken);
expect(assetsAfterEmpty.length).toBe(0);
});
it('should restore all trashed assets', async () => {
const content = await readFile(assetFilePath);
const { id: assetId } = await api.assetApi.upload(server, admin.accessToken, 'test-device-id', {
content,
filename: basename(assetFilePath),
});
await api.assetApi.delete(server, admin.accessToken, { ids: [assetId] });
const deletedAsset = await api.assetApi.get(server, admin.accessToken, assetId);
expect(deletedAsset.isTrashed).toBe(true);
await api.trashApi.restore(server, admin.accessToken);
const restoredAsset = await api.assetApi.get(server, admin.accessToken, assetId);
expect(restoredAsset.isTrashed).toBe(false);
});
});
+9 -2802
View File
File diff suppressed because it is too large Load Diff
-2
View File
@@ -34,8 +34,6 @@
"sql:generate": "node ./dist/infra/sql-generator/"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.515.0",
"@aws-sdk/lib-storage": "^3.515.0",
"@babel/runtime": "^7.22.11",
"@immich/cli": "^2.0.7",
"@nestjs/bullmq": "^10.0.1",
@@ -286,7 +286,6 @@ describe(AssetService.name, () => {
describe('getMapMarkers', () => {
it('should get geo information of assets', async () => {
partnerMock.getAll.mockResolvedValue([]);
assetMock.getMapMarkers.mockResolvedValue(
[assetStub.withLocation].map((asset) => ({
id: asset.id,
+2 -10
View File
@@ -157,16 +157,8 @@ export class AssetService {
return folder;
}
async getMapMarkers(auth: AuthDto, options: MapMarkerDto): Promise<MapMarkerResponseDto[]> {
const userIds: string[] = [auth.user.id];
if (options.withPartners) {
const partners = await this.partnerRepository.getAll(auth.user.id);
const partnersIds = partners
.filter((partner) => partner.sharedBy && partner.sharedWith && partner.sharedById != auth.user.id)
.map((partner) => partner.sharedById);
userIds.push(...partnersIds);
}
return this.assetRepository.getMapMarkers(userIds, options);
getMapMarkers(auth: AuthDto, options: MapMarkerDto): Promise<MapMarkerResponseDto[]> {
return this.assetRepository.getMapMarkers(auth.user.id, options);
}
async getMemoryLane(auth: AuthDto, dto: MemoryLaneDto): Promise<MemoryLaneResponseDto[]> {
@@ -25,10 +25,4 @@ export class MapMarkerDto {
@IsDate()
@Type(() => Date)
fileCreatedBefore?: Date;
@ApiProperty()
@Optional()
@IsBoolean()
@Transform(toBoolean)
withPartners?: boolean;
}
-27
View File
@@ -1,27 +0,0 @@
import { Readable, Writable } from "node:stream";
export interface FS {
// create creates an object with the given name.
create(name: string): Promise<Writable>;
// open opens the named object.
open(name: string): Promise<Readable>;
// remove removes the named object.
remove(name: string): Promise<void>;
}
// export interface FS {
// // create creates an object with the given name.
// create(name: string): Promise<WritableFile>;
// // open opens the object with the given name.
// open(name: string): Promise<ReadableFile>;
// // remove removes the named object.
// remove(name: string): Promise<void>;
// }
// export interface File {
// createReadableStream(): Promise<Readable>;
// }
-21
View File
@@ -1,21 +0,0 @@
import { constants, open, unlink } from "node:fs/promises";
import { join } from "node:path";
import { Readable, Writable } from "node:stream";
export class LocalFS {
constructor(private dir: string) { }
async create(name: string): Promise<Writable> {
const file = await open(join(this.dir, name), constants.O_WRONLY);
return file.createWriteStream();
}
async open(name: string): Promise<Readable> {
const file = await open(join(this.dir, name), constants.O_RDONLY);
return file.createReadStream();
}
async remove(name: string): Promise<void> {
await unlink(join(this.dir, name));
}
}
-65
View File
@@ -1,65 +0,0 @@
import { PassThrough, Readable, Writable } from "node:stream";
import { S3 } from "@aws-sdk/client-s3";
import { FS } from "./fs";
import { Upload } from "@aws-sdk/lib-storage";
export class S3FS implements FS {
s3: S3;
constructor(private bucket: string) {
this.s3 = new S3();
}
async create(name: string): Promise<Writable> {
const stream = new PassThrough();
const upload = new Upload({
client: this.s3,
params: {
Body: stream,
Bucket: this.bucket,
Key: name,
},
});
// Abort the upload if the stream has finished. Should be a
// no-op if the upload has already finished.
stream.on('close', () => upload.abort());
// Close the stream when the upload is finished, or if it
// failed.
//
// TODO: Find a way to bubble up this error.
upload.done().then(() => void stream.end(), error => {
console.log(`s3 upload failed: ${error}`);
stream.end();
});
return stream;
}
async open(name: string): Promise<Readable> {
const obj = await this.s3.getObject({
Bucket: this.bucket,
Key: name,
});
return obj.Body as Readable;
// const stream = obj.Body?.transformToWebStream();
// if (!stream) {
// throw new Error("no body");
// }
// return Readable.fromWeb(new ReadableStream(stream));
}
async remove(name: string): Promise<void> {
await this.s3.deleteObject({
Bucket: this.bucket,
Key: name,
})
}
}
// class ObjectReadable extends Readable {
// constructor(private s3: S3, private bucket: string) { }
// }
+9 -36
View File
@@ -704,35 +704,8 @@ describe(MediaService.name, () => {
);
});
it('should copy video stream when video matches target', async () => {
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.HEVC }]);
assetMock.getByIds.mockResolvedValue([assetStub.video]);
await sut.handleVideoConversion({ id: assetStub.video.id });
expect(mediaMock.transcode).toHaveBeenCalledWith(
'/original/path.ext',
'upload/encoded-video/user-id/as/se/asset-id.mp4',
{
inputOptions: [],
outputOptions: [
'-c:v copy',
'-c:a aac',
'-movflags faststart',
'-fps_mode passthrough',
'-map 0:0',
'-map 0:1',
'-tag:v hvc1',
'-v verbose',
'-preset ultrafast',
'-crf 23',
],
twoPass: false,
},
);
});
it('should copy audio stream when audio matches target', async () => {
mediaMock.probe.mockResolvedValue(probeStub.audioStreamAac);
it('should transcode when audio doesnt match target', async () => {
mediaMock.probe.mockResolvedValue(probeStub.audioStreamMp3);
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.OPTIMAL }]);
assetMock.getByIds.mockResolvedValue([assetStub.video]);
await sut.handleVideoConversion({ id: assetStub.video.id });
@@ -743,7 +716,7 @@ describe(MediaService.name, () => {
inputOptions: [],
outputOptions: [
'-c:v h264',
'-c:a copy',
'-c:a aac',
'-movflags faststart',
'-fps_mode passthrough',
'-map 0:0',
@@ -785,11 +758,11 @@ describe(MediaService.name, () => {
);
});
it('should throw an exception if transcode value is invalid', async () => {
it('should not transcode an invalid transcode value', async () => {
mediaMock.probe.mockResolvedValue(probeStub.videoStream2160p);
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: 'invalid' }]);
await expect(sut.handleVideoConversion({ id: assetStub.video.id })).rejects.toThrow();
assetMock.getByIds.mockResolvedValue([assetStub.video]);
await sut.handleVideoConversion({ id: assetStub.video.id });
expect(mediaMock.transcode).not.toHaveBeenCalled();
});
@@ -1133,7 +1106,7 @@ describe(MediaService.name, () => {
});
it('should disable thread pooling for hevc if thread limit is above 0', async () => {
mediaMock.probe.mockResolvedValue(probeStub.videoStreamVp9);
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
configMock.load.mockResolvedValue([
{ key: SystemConfigKey.FFMPEG_THREADS, value: 2 },
{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.HEVC },
@@ -1167,7 +1140,7 @@ describe(MediaService.name, () => {
});
it('should omit thread flags for hevc if thread limit is at or below 0', async () => {
mediaMock.probe.mockResolvedValue(probeStub.videoStreamVp9);
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
configMock.load.mockResolvedValue([
{ key: SystemConfigKey.FFMPEG_THREADS, value: 0 },
{ key: SystemConfigKey.FFMPEG_TARGET_VIDEO_CODEC, value: VideoCodec.HEVC },
@@ -1783,7 +1756,7 @@ describe(MediaService.name, () => {
it('should set vbr options for rkmpp when max bitrate is enabled', async () => {
storageMock.readdir.mockResolvedValue(['renderD128']);
mediaMock.probe.mockResolvedValue(probeStub.videoStreamVp9);
mediaMock.probe.mockResolvedValue(probeStub.matroskaContainer);
configMock.load.mockResolvedValue([
{ key: SystemConfigKey.FFMPEG_ACCEL, value: TranscodeHWAccel.RKMPP },
{ key: SystemConfigKey.FFMPEG_MAX_BITRATE, value: '10000k' },
+43 -73
View File
@@ -6,7 +6,6 @@ import {
Colorspace,
TranscodeHWAccel,
TranscodePolicy,
TranscodeTarget,
VideoCodec,
} from '@app/infra/entities';
import { ImmichLogger } from '@app/infra/logger';
@@ -198,7 +197,7 @@ export class MediaService {
}
const mainAudioStream = this.getMainStream(audioStreams);
const config = { ...ffmpeg, targetResolution: size.toString() };
const options = new ThumbnailConfig(config).getOptions(TranscodeTarget.VIDEO, mainVideoStream, mainAudioStream);
const options = new ThumbnailConfig(config).getOptions(mainVideoStream, mainAudioStream);
await this.mediaRepository.transcode(asset.originalPath, path, options);
break;
}
@@ -268,6 +267,7 @@ export class MediaService {
const mainVideoStream = this.getMainStream(videoStreams);
const mainAudioStream = this.getMainStream(audioStreams);
const containerExtension = format.formatName;
const bitrate = format.bitrate;
if (!mainVideoStream || !containerExtension) {
return false;
}
@@ -279,8 +279,15 @@ export class MediaService {
const { ffmpeg: config } = await this.configCore.getConfig();
const target = this.getTranscodeTarget(config, mainVideoStream, mainAudioStream);
if (target === TranscodeTarget.NONE) {
const required = this.isTranscodeRequired(
asset,
mainVideoStream,
mainAudioStream,
containerExtension,
config,
bitrate,
);
if (!required) {
if (asset.encodedVideoPath) {
this.logger.log(`Transcoded video exists for asset ${asset.id}, but is no longer required. Deleting...`);
await this.jobRepository.queue({ name: JobName.DELETE_FILES, data: { files: [asset.encodedVideoPath] } });
@@ -292,15 +299,13 @@ export class MediaService {
let transcodeOptions;
try {
transcodeOptions = await this.getCodecConfig(config).then((c) =>
c.getOptions(target, mainVideoStream, mainAudioStream),
);
transcodeOptions = await this.getCodecConfig(config).then((c) => c.getOptions(mainVideoStream, mainAudioStream));
} catch (error) {
this.logger.error(`An error occurred while configuring transcoding options: ${error}`);
return false;
}
this.logger.log(`Started encoding video ${asset.id} ${JSON.stringify(transcodeOptions)}`);
this.logger.log(`Start encoding video ${asset.id} ${JSON.stringify(transcodeOptions)}`);
try {
await this.mediaRepository.transcode(input, output, transcodeOptions);
} catch (error) {
@@ -311,13 +316,11 @@ export class MediaService {
);
}
config.accel = TranscodeHWAccel.DISABLED;
transcodeOptions = await this.getCodecConfig(config).then((c) =>
c.getOptions(target, mainVideoStream, mainAudioStream),
);
transcodeOptions = await this.getCodecConfig(config).then((c) => c.getOptions(mainVideoStream, mainAudioStream));
await this.mediaRepository.transcode(input, output, transcodeOptions);
}
this.logger.log(`Successfully encoded ${asset.id}`);
this.logger.log(`Encoding success ${asset.id}`);
await this.assetRepository.save({ id: asset.id, encodedVideoPath: output });
@@ -328,87 +331,54 @@ export class MediaService {
return streams.sort((stream1, stream2) => stream2.frameCount - stream1.frameCount)[0];
}
private getTranscodeTarget(
config: SystemConfigFFmpegDto,
videoStream: VideoStreamInfo | null,
private isTranscodeRequired(
asset: AssetEntity,
videoStream: VideoStreamInfo,
audioStream: AudioStreamInfo | null,
): TranscodeTarget {
if (videoStream == null && audioStream == null) {
return TranscodeTarget.NONE;
}
containerExtension: string,
ffmpegConfig: SystemConfigFFmpegDto,
bitrate: number,
): boolean {
const isTargetVideoCodec = ffmpegConfig.acceptedVideoCodecs.includes(videoStream.codecName as VideoCodec);
const isTargetContainer = ['mov,mp4,m4a,3gp,3g2,mj2', 'mp4', 'mov'].includes(containerExtension);
const isTargetAudioCodec =
audioStream == null || ffmpegConfig.acceptedAudioCodecs.includes(audioStream.codecName as AudioCodec);
const isAudioTranscodeRequired = this.isAudioTranscodeRequired(config, audioStream);
const isVideoTranscodeRequired = this.isVideoTranscodeRequired(config, videoStream);
if (isAudioTranscodeRequired && isVideoTranscodeRequired) {
return TranscodeTarget.ALL;
}
if (isAudioTranscodeRequired) {
return TranscodeTarget.AUDIO;
}
if (isVideoTranscodeRequired) {
return TranscodeTarget.VIDEO;
}
return TranscodeTarget.NONE;
}
private isAudioTranscodeRequired(ffmpegConfig: SystemConfigFFmpegDto, stream: AudioStreamInfo | null): boolean {
if (stream == null) {
return false;
}
switch (ffmpegConfig.transcode) {
case TranscodePolicy.DISABLED: {
return false;
}
case TranscodePolicy.ALL: {
return true;
}
case TranscodePolicy.REQUIRED:
case TranscodePolicy.OPTIMAL:
case TranscodePolicy.BITRATE: {
return !ffmpegConfig.acceptedAudioCodecs.includes(stream.codecName as AudioCodec);
}
default: {
throw new Error(`Unsupported transcode policy: ${ffmpegConfig.transcode}`);
}
}
}
private isVideoTranscodeRequired(ffmpegConfig: SystemConfigFFmpegDto, stream: VideoStreamInfo | null): boolean {
if (stream == null) {
return false;
}
this.logger.verbose(
`${asset.id}: AudioCodecName ${audioStream?.codecName ?? 'None'}, AudioStreamCodecType ${
audioStream?.codecType ?? 'None'
}, containerExtension ${containerExtension}`,
);
const allTargetsMatching = isTargetVideoCodec && isTargetAudioCodec && isTargetContainer;
const scalingEnabled = ffmpegConfig.targetResolution !== 'original';
const targetRes = Number.parseInt(ffmpegConfig.targetResolution);
const isLargerThanTargetRes = scalingEnabled && Math.min(stream.height, stream.width) > targetRes;
const isLargerThanTargetBitrate = stream.bitrate > this.parseBitrateToBps(ffmpegConfig.maxBitrate);
const isTargetVideoCodec = ffmpegConfig.acceptedVideoCodecs.includes(stream.codecName as VideoCodec);
const isRequired = !isTargetVideoCodec || stream.isHDR;
const isLargerThanTargetRes = scalingEnabled && Math.min(videoStream.height, videoStream.width) > targetRes;
const isLargerThanTargetBitrate = bitrate > this.parseBitrateToBps(ffmpegConfig.maxBitrate);
switch (ffmpegConfig.transcode) {
case TranscodePolicy.DISABLED: {
return false;
}
case TranscodePolicy.ALL: {
return true;
}
case TranscodePolicy.REQUIRED: {
return isRequired;
return !allTargetsMatching || videoStream.isHDR;
}
case TranscodePolicy.OPTIMAL: {
return isRequired || isLargerThanTargetRes;
return !allTargetsMatching || isLargerThanTargetRes || videoStream.isHDR;
}
case TranscodePolicy.BITRATE: {
return isRequired || isLargerThanTargetBitrate;
return !allTargetsMatching || isLargerThanTargetBitrate || videoStream.isHDR;
}
default: {
throw new Error(`Unsupported transcode policy: ${ffmpegConfig.transcode}`);
return false;
}
}
}
+16 -21
View File
@@ -1,4 +1,4 @@
import { CQMode, ToneMapping, TranscodeHWAccel, TranscodeTarget, VideoCodec } from '@app/infra/entities';
import { CQMode, ToneMapping, TranscodeHWAccel, VideoCodec } from '@app/infra/entities';
import {
AudioStreamInfo,
BitrateDistribution,
@@ -12,19 +12,16 @@ class BaseConfig implements VideoCodecSWConfig {
presets = ['veryslow', 'slower', 'slow', 'medium', 'fast', 'faster', 'veryfast', 'superfast', 'ultrafast'];
constructor(protected config: SystemConfigFFmpegDto) {}
getOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) {
getOptions(videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) {
const options = {
inputOptions: this.getBaseInputOptions(),
outputOptions: [...this.getBaseOutputOptions(target, videoStream, audioStream), '-v verbose'],
outputOptions: [...this.getBaseOutputOptions(videoStream, audioStream), '-v verbose'],
twoPass: this.eligibleForTwoPass(),
} as TranscodeOptions;
if ([TranscodeTarget.ALL, TranscodeTarget.VIDEO].includes(target)) {
const filters = this.getFilterOptions(videoStream);
if (filters.length > 0) {
options.outputOptions.push(`-vf ${filters.join(',')}`);
}
const filters = this.getFilterOptions(videoStream);
if (filters.length > 0) {
options.outputOptions.push(`-vf ${filters.join(',')}`);
}
options.outputOptions.push(...this.getPresetOptions(), ...this.getThreadOptions(), ...this.getBitrateOptions());
return options;
@@ -34,10 +31,10 @@ class BaseConfig implements VideoCodecSWConfig {
return [];
}
getBaseOutputOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) {
getBaseOutputOptions(videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) {
const options = [
`-c:v ${[TranscodeTarget.ALL, TranscodeTarget.VIDEO].includes(target) ? this.getVideoCodec() : 'copy'}`,
`-c:a ${[TranscodeTarget.ALL, TranscodeTarget.AUDIO].includes(target) ? this.getAudioCodec() : 'copy'}`,
`-c:v ${this.getVideoCodec()}`,
`-c:a ${this.getAudioCodec()}`,
// Makes a second pass moving the moov atom to the
// beginning of the file for improved playback speed.
'-movflags faststart',
@@ -401,14 +398,14 @@ export class NVENCConfig extends BaseHWConfig {
return ['-init_hw_device cuda=cuda:0', '-filter_hw_device cuda'];
}
getBaseOutputOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) {
getBaseOutputOptions(videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) {
const options = [
// below settings recommended from https://docs.nvidia.com/video-technologies/video-codec-sdk/12.0/ffmpeg-with-nvidia-gpu/index.html#command-line-for-latency-tolerant-high-quality-transcoding
'-tune hq',
'-qmin 0',
'-rc-lookahead 20',
'-i_qfactor 0.75',
...super.getBaseOutputOptions(target, videoStream, audioStream),
...super.getBaseOutputOptions(videoStream, audioStream),
];
if (this.getBFrames() > 0) {
options.push('-b_ref_mode middle', '-b_qfactor 1.1');
@@ -486,8 +483,8 @@ export class QSVConfig extends BaseHWConfig {
return [`-init_hw_device qsv=hw${qsvString}`, '-filter_hw_device hw'];
}
getBaseOutputOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) {
const options = super.getBaseOutputOptions(target, videoStream, audioStream);
getBaseOutputOptions(videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) {
const options = super.getBaseOutputOptions(videoStream, audioStream);
// VP9 requires enabling low power mode https://git.ffmpeg.org/gitweb/ffmpeg.git/commit/33583803e107b6d532def0f9d949364b01b6ad5a
if (this.config.targetVideoCodec === VideoCodec.VP9) {
options.push('-low_power 1');
@@ -607,13 +604,11 @@ export class VAAPIConfig extends BaseHWConfig {
}
export class RKMPPConfig extends BaseHWConfig {
getOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo): TranscodeOptions {
const options = super.getOptions(target, videoStream, audioStream);
getOptions(videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo): TranscodeOptions {
const options = super.getOptions(videoStream, audioStream);
options.ffmpegPath = 'ffmpeg_mpp';
options.ldLibraryPath = '/lib/aarch64-linux-gnu:/lib/ffmpeg-mpp';
if ([TranscodeTarget.ALL, TranscodeTarget.VIDEO].includes(target)) {
options.outputOptions.push(...this.getSizeOptions(videoStream));
}
options.outputOptions.push(...this.getSizeOptions(videoStream));
return options;
}
+2 -2
View File
@@ -410,8 +410,8 @@ export class PersonService {
});
// `matches` also includes the face itself
if (machineLearning.facialRecognition.minFaces > 1 && matches.length <= 1) {
this.logger.debug(`Face ${id} only matched the face itself, skipping`);
if (matches.length <= 1) {
this.logger.debug(`Face ${id} has no matches`);
return true;
}
@@ -140,7 +140,7 @@ export interface IAssetRepository {
softDeleteAll(ids: string[]): Promise<void>;
restoreAll(ids: string[]): Promise<void>;
findLivePhotoMatch(options: LivePhotoSearchOptions): Promise<AssetEntity | null>;
getMapMarkers(ownerIds: string[], options?: MapMarkerSearchOptions): Promise<MapMarker[]>;
getMapMarkers(ownerId: string, options?: MapMarkerSearchOptions): Promise<MapMarker[]>;
getStatistics(ownerId: string, options: AssetStatsOptions): Promise<AssetStats>;
getTimeBuckets(options: TimeBucketOptions): Promise<TimeBucketItem[]>;
getTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise<AssetEntity[]>;
@@ -1,4 +1,4 @@
import { TranscodeTarget, VideoCodec } from '@app/infra/entities';
import { VideoCodec } from '@app/infra/entities';
import { Writable } from 'node:stream';
export const IMediaRepository = 'IMediaRepository';
@@ -16,14 +16,15 @@ export interface VideoStreamInfo {
width: number;
rotation: number;
codecName?: string;
codecType?: string;
frameCount: number;
isHDR: boolean;
bitrate: number;
}
export interface AudioStreamInfo {
index: number;
codecName?: string;
codecType?: string;
frameCount: number;
}
@@ -63,7 +64,7 @@ export interface BitrateDistribution {
}
export interface VideoCodecSWConfig {
getOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream: AudioStreamInfo): TranscodeOptions;
getOptions(videoStream: VideoStreamInfo, audioStream: AudioStreamInfo): TranscodeOptions;
}
export interface VideoCodecHWConfig extends VideoCodecSWConfig {
@@ -66,14 +66,13 @@ export interface SearchAssetIDOptions {
id?: string;
}
export interface SearchUserIdOptions {
export interface SearchUserIDOptions {
deviceId?: string;
libraryId?: string;
ownerId?: string;
userIds?: string[];
}
export type SearchIdOptions = SearchAssetIDOptions & SearchUserIdOptions;
export type SearchIDOptions = SearchAssetIDOptions & SearchUserIDOptions;
export interface SearchStatusOptions {
isArchived?: boolean;
@@ -84,7 +83,6 @@ export interface SearchStatusOptions {
isOffline?: boolean;
isReadOnly?: boolean;
isVisible?: boolean;
isNotInAlbum?: boolean;
type?: AssetType;
withArchived?: boolean;
withDeleted?: boolean;
@@ -134,10 +132,6 @@ export interface SearchEmbeddingOptions {
userIds: string[];
}
export interface SearchPeopleOptions {
personIds?: string[];
}
export interface SearchOrderOptions {
orderDirection?: 'ASC' | 'DESC';
}
@@ -148,14 +142,12 @@ export interface SearchPaginationOptions {
}
export type AssetSearchOptions = SearchDateOptions &
SearchIdOptions &
SearchIDOptions &
SearchExifOptions &
SearchOrderOptions &
SearchPathOptions &
SearchRelationOptions &
SearchStatusOptions &
SearchUserIdOptions &
SearchPeopleOptions;
SearchStatusOptions;
export type AssetSearchBuilderOptions = Omit<AssetSearchOptions, 'orderDirection'>;
@@ -164,8 +156,7 @@ export type SmartSearchOptions = SearchDateOptions &
SearchExifOptions &
SearchOneToOneRelationOptions &
SearchStatusOptions &
SearchUserIdOptions &
SearchPeopleOptions;
SearchUserIDOptions;
export interface FaceEmbeddingSearch extends SearchEmbeddingOptions {
hasPerson?: boolean;
@@ -169,12 +169,6 @@ export class MetadataSearchDto extends BaseSearchDto {
@Optional()
@ApiProperty({ enumName: 'AssetOrder', enum: AssetOrder })
order?: AssetOrder;
@QueryBoolean({ optional: true })
isNotInAlbum?: boolean;
@Optional()
personIds?: string[];
}
export class SmartSearchDto extends BaseSearchDto {
+1 -2
View File
@@ -60,7 +60,6 @@ export class SearchService {
async searchMetadata(auth: AuthDto, dto: MetadataSearchDto): Promise<SearchResponseDto> {
let checksum: Buffer | undefined;
const userIds = await this.getUserIdsToSearch(auth);
if (dto.checksum) {
const encoding = dto.checksum.length === 28 ? 'base64' : 'hex';
@@ -75,7 +74,7 @@ export class SearchService {
{
...dto,
checksum,
userIds,
ownerId: auth.user.id,
orderDirection: dto.order ? enumToOrder[dto.order] : 'DESC',
},
);
@@ -140,12 +140,6 @@ export const defaults = Object.freeze<SystemConfig>({
externalDomain: '',
loginPageMessage: '',
},
storage: {
kind: 'local',
options: {
path: process.env.UPLOAD_LOCATION ?? '',
}
}
});
export enum FeatureFlag {
@@ -174,7 +168,7 @@ export class SystemConfigCore {
public config$ = new Subject<SystemConfig>();
private constructor(private repository: ISystemConfigRepository) { }
private constructor(private repository: ISystemConfigRepository) {}
static create(repository: ISystemConfigRepository) {
if (!instance) {
@@ -10,7 +10,7 @@ import {
SmartSearchDto,
} from '@app/domain';
import { SearchSuggestionRequestDto } from '@app/domain/search/dto/search-suggestion.dto';
import { Body, Controller, Get, Post, Query } from '@nestjs/common';
import { Controller, Get, Query } from '@nestjs/common';
import { ApiOperation, ApiTags } from '@nestjs/swagger';
import { Auth, Authenticated } from '../app.guard';
import { UseValidation } from '../app.utils';
@@ -22,13 +22,13 @@ import { UseValidation } from '../app.utils';
export class SearchController {
constructor(private service: SearchService) {}
@Post('metadata')
searchMetadata(@Auth() auth: AuthDto, @Body() dto: MetadataSearchDto): Promise<SearchResponseDto> {
@Get('metadata')
searchMetadata(@Auth() auth: AuthDto, @Query() dto: MetadataSearchDto): Promise<SearchResponseDto> {
return this.service.searchMetadata(auth, dto);
}
@Post('smart')
searchSmart(@Auth() auth: AuthDto, @Body() dto: SmartSearchDto): Promise<SearchResponseDto> {
@Get('smart')
searchSmart(@Auth() auth: AuthDto, @Query() dto: SmartSearchDto): Promise<SearchResponseDto> {
return this.service.searchSmart(auth, dto);
}
@@ -118,13 +118,6 @@ export enum TranscodePolicy {
DISABLED = 'disabled',
}
export enum TranscodeTarget {
NONE,
AUDIO,
VIDEO,
ALL,
}
export enum VideoCodec {
H264 = 'h264',
HEVC = 'hevc',
@@ -172,18 +165,6 @@ export enum LogLevel {
FATAL = 'fatal',
}
export interface StorageOptionsLocal {
path: string;
}
export interface StorageOptionsS3 {
bucket: string;
region: string;
endpoint: string;
accessKeyId: string;
secretAccessKey: string;
}
export interface SystemConfig {
ffmpeg: {
crf: number;
@@ -288,10 +269,4 @@ export interface SystemConfig {
externalDomain: string;
loginPageMessage: string;
};
// TODO(uhthomas): Is this definitely the approach we want to take for
// configuring storage?
storage: {
kind: string;
options: StorageOptionsLocal | StorageOptionsS3;
}
}
+5 -33
View File
@@ -139,27 +139,14 @@ export function searchAssetBuilder(
);
const exifInfo = _.omitBy(_.pick(options, ['city', 'country', 'lensModel', 'make', 'model', 'state']), _.isUndefined);
const hasExifQuery = Object.keys(exifInfo).length > 0;
if (options.withExif && !hasExifQuery) {
builder.leftJoinAndSelect(`${builder.alias}.exifInfo`, 'exifInfo');
}
if (hasExifQuery) {
options.withExif
? builder.leftJoinAndSelect(`${builder.alias}.exifInfo`, 'exifInfo')
: builder.leftJoin(`${builder.alias}.exifInfo`, 'exifInfo');
if (Object.keys(exifInfo).length > 0) {
builder.leftJoin(`${builder.alias}.exifInfo`, 'exifInfo');
builder.andWhere({ exifInfo });
}
const id = _.pick(options, ['checksum', 'deviceAssetId', 'deviceId', 'id', 'libraryId']);
const id = _.pick(options, ['checksum', 'deviceAssetId', 'deviceId', 'id', 'libraryId', 'ownerId']);
builder.andWhere(_.omitBy(id, _.isUndefined));
if (options.userIds) {
builder.andWhere(`${builder.alias}.ownerId IN (:...userIds)`, { userIds: options.userIds });
}
const path = _.pick(options, ['encodedVideoPath', 'originalFileName', 'originalPath', 'resizePath', 'webpPath']);
builder.andWhere(_.omitBy(path, _.isUndefined));
@@ -177,11 +164,8 @@ export function searchAssetBuilder(
),
);
if (options.isNotInAlbum) {
builder
.leftJoin(`${builder.alias}.albums`, 'albums')
.andWhere('albums.id IS NULL')
.andWhere(`${builder.alias}.isVisible = true`);
if (options.withExif) {
builder.leftJoinAndSelect(`${builder.alias}.exifInfo`, 'exifInfo');
}
if (options.withFaces || options.withPeople) {
@@ -196,18 +180,6 @@ export function searchAssetBuilder(
builder.leftJoinAndSelect(`${builder.alias}.smartInfo`, 'smartInfo');
}
if (options.personIds && options.personIds.length > 0) {
builder
.leftJoin(`${builder.alias}.faces`, 'faces')
.andWhere('faces.personId IN (:...personIds)', { personIds: options.personIds })
.addGroupBy(`${builder.alias}.id`)
.having('COUNT(faces.id) = :personCount', { personCount: options.personIds.length });
if (options.withExif) {
builder.addGroupBy('exifInfo.assetId');
}
}
if (options.withStacked) {
builder
.leftJoinAndSelect(`${builder.alias}.stack`, 'stack')
@@ -176,7 +176,7 @@ export class AssetRepository implements IAssetRepository {
}
getByUserId(pagination: PaginationOptions, userId: string, options: AssetSearchOptions = {}): Paginated<AssetEntity> {
return this.getAll(pagination, { ...options, ownerId: userId });
return this.getAll(pagination, { ...options, id: userId });
}
@GenerateSql({ params: [[DummyValue.UUID]] })
@@ -472,7 +472,7 @@ export class AssetRepository implements IAssetRepository {
});
}
async getMapMarkers(ownerIds: string[], options: MapMarkerSearchOptions = {}): Promise<MapMarker[]> {
async getMapMarkers(ownerId: string, options: MapMarkerSearchOptions = {}): Promise<MapMarker[]> {
const { isArchived, isFavorite, fileCreatedAfter, fileCreatedBefore } = options;
const assets = await this.repository.find({
@@ -484,7 +484,7 @@ export class AssetRepository implements IAssetRepository {
},
},
where: {
ownerId: In([...ownerIds]),
ownerId,
isVisible: true,
isArchived,
exifInfo: {
@@ -60,7 +60,6 @@ export class MediaRepository implements IMediaRepository {
frameCount: Number.parseInt(stream.nb_frames ?? '0'),
rotation: Number.parseInt(`${stream.rotation ?? 0}`),
isHDR: stream.color_transfer === 'smpte2084' || stream.color_transfer === 'arib-std-b67',
bitrate: Number.parseInt(stream.bit_rate ?? '0'),
})),
audioStreams: results.streams
.filter((stream) => stream.codec_type === 'audio')
@@ -58,7 +58,6 @@ export class SearchRepository implements ISearchRepository {
ownerId: DummyValue.UUID,
withStacked: true,
isFavorite: true,
ownerIds: [DummyValue.UUID],
},
],
})
@@ -67,6 +66,7 @@ export class SearchRepository implements ISearchRepository {
builder = searchAssetBuilder(builder, options);
builder.orderBy('asset.fileCreatedAt', options.orderDirection ?? 'DESC');
return paginatedBuilder<AssetEntity>(builder, {
mode: PaginationMode.SKIP_TAKE,
skip: (pagination.page - 1) * pagination.size,
+2 -2
View File
@@ -77,9 +77,9 @@ FROM
(
"asset"."fileCreatedAt" >= $1
AND "exifInfo"."lensModel" = $2
AND "asset"."ownerId" = $3
AND 1 = 1
AND 1 = 1
AND "asset"."isFavorite" = $3
AND "asset"."isFavorite" = $4
AND (
"stack"."primaryAssetId" = "asset"."id"
OR "asset"."stackId" IS NULL
+20 -23
View File
@@ -13,14 +13,16 @@ const probeStubDefaultVideoStream: VideoStreamInfo[] = [
height: 1080,
width: 1920,
codecName: 'hevc',
codecType: 'video',
frameCount: 100,
rotation: 0,
isHDR: false,
bitrate: 0,
},
];
const probeStubDefaultAudioStream: AudioStreamInfo[] = [{ index: 1, codecName: 'mp3', frameCount: 100 }];
const probeStubDefaultAudioStream: AudioStreamInfo[] = [
{ index: 1, codecName: 'aac', codecType: 'audio', frameCount: 100 },
];
const probeStubDefault: VideoInfo = {
format: probeStubDefaultFormat,
@@ -39,20 +41,20 @@ export const probeStub = {
height: 1080,
width: 400,
codecName: 'hevc',
codecType: 'video',
frameCount: 100,
rotation: 0,
isHDR: false,
bitrate: 0,
},
{
index: 1,
height: 1080,
width: 400,
codecName: 'h7000',
codecType: 'video',
frameCount: 99,
rotation: 0,
isHDR: false,
bitrate: 0,
},
],
}),
@@ -64,10 +66,10 @@ export const probeStub = {
height: 0,
width: 400,
codecName: 'hevc',
codecType: 'video',
frameCount: 100,
rotation: 0,
isHDR: false,
bitrate: 0,
},
],
}),
@@ -79,16 +81,21 @@ export const probeStub = {
height: 2160,
width: 3840,
codecName: 'h264',
codecType: 'video',
frameCount: 100,
rotation: 0,
isHDR: false,
bitrate: 0,
},
],
}),
videoStream40Mbps: Object.freeze<VideoInfo>({
...probeStubDefault,
videoStreams: [{ ...probeStubDefaultVideoStream[0], bitrate: 40_000_000 }],
format: {
formatName: 'mov,mp4,m4a,3gp,3g2,mj2',
formatLongName: 'QuickTime / MOV',
duration: 0,
bitrate: 40_000_000,
},
}),
videoStreamHDR: Object.freeze<VideoInfo>({
...probeStubDefault,
@@ -98,10 +105,10 @@ export const probeStub = {
height: 480,
width: 480,
codecName: 'h264',
codecType: 'video',
frameCount: 100,
rotation: 0,
isHDR: true,
bitrate: 0,
},
],
}),
@@ -113,10 +120,10 @@ export const probeStub = {
height: 2160,
width: 3840,
codecName: 'h264',
codecType: 'video',
frameCount: 100,
rotation: 90,
isHDR: false,
bitrate: 0,
},
],
}),
@@ -128,10 +135,10 @@ export const probeStub = {
height: 355,
width: 1586,
codecName: 'h264',
codecType: 'video',
frameCount: 100,
rotation: 0,
isHDR: false,
bitrate: 0,
},
],
}),
@@ -143,16 +150,16 @@ export const probeStub = {
height: 1586,
width: 355,
codecName: 'h264',
codecType: 'video',
frameCount: 100,
rotation: 0,
isHDR: false,
bitrate: 0,
},
],
}),
audioStreamAac: Object.freeze<VideoInfo>({
audioStreamMp3: Object.freeze<VideoInfo>({
...probeStubDefault,
audioStreams: [{ index: 1, codecName: 'aac', frameCount: 100 }],
audioStreams: [{ index: 1, codecType: 'audio', codecName: 'aac', frameCount: 100 }],
}),
matroskaContainer: Object.freeze<VideoInfo>({
...probeStubDefault,
@@ -163,14 +170,4 @@ export const probeStub = {
bitrate: 0,
},
}),
videoStreamVp9: Object.freeze<VideoInfo>({
...probeStubDefault,
videoStreams: [{ ...probeStubDefaultVideoStream[0], codecName: 'vp9' }],
format: {
formatName: 'matroska,webm',
formatLongName: 'Matroska / WebM',
duration: 0,
bitrate: 0,
},
}),
};
-1
View File
@@ -38,7 +38,6 @@ module.exports = {
'unicorn/prevent-abbreviations': 'off',
'unicorn/no-nested-ternary': 'off',
'unicorn/consistent-function-scoping': 'off',
'unicorn/prefer-top-level-await': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{
+1 -2
View File
@@ -3,7 +3,6 @@
"trailingComma": "all",
"printWidth": 120,
"semi": true,
"plugins": ["prettier-plugin-organize-imports", "prettier-plugin-svelte"],
"organizeImportsSkipDestructiveCodeActions": true,
"plugins": ["prettier-plugin-svelte"],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}
-2
View File
@@ -1,6 +1,5 @@
FROM node:iron-alpine3.18
RUN apk add --no-cache tini
USER node
WORKDIR /usr/src/app
COPY --chown=node:node package*.json ./
@@ -9,4 +8,3 @@ COPY --chown=node:node . .
ENV CHOKIDAR_USEPOLLING=true
EXPOSE 24678
EXPOSE 3000
ENTRYPOINT ["/sbin/tini", "--", "/bin/sh"]
+7 -2
View File
@@ -2,7 +2,12 @@
TYPESCRIPT_SDK=/usr/src/open-api/typescript-sdk
npm --prefix "$TYPESCRIPT_SDK" install
npm --prefix "$TYPESCRIPT_SDK" run build
if [ ! -d "$TYPESCRIPT_SDK/build" ]; then
echo "$TYPESCRIPT_SDK/build does not exist, building"
npm --prefix "$TYPESCRIPT_SDK" install
npm --prefix "$TYPESCRIPT_SDK" run build
else
echo "$TYPESCRIPT_SDK/build exists, skipping"
fi
node ./node_modules/.bin/vite dev --host 0.0.0.0 --port 3000
+35 -74
View File
@@ -23,16 +23,15 @@
"luxon": "^3.2.1",
"socket.io-client": "^4.6.1",
"svelte-local-storage-store": "^0.6.0",
"svelte-maplibre": "^0.8.0",
"svelte-maplibre": "^0.7.0",
"thumbhash": "^0.1.1"
},
"devDependencies": {
"@faker-js/faker": "^8.0.0",
"@floating-ui/dom": "^1.5.1",
"@socket.io/component-emitter": "^3.1.0",
"@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^2.5.0",
"@sveltejs/vite-plugin-svelte": "^3.0.2",
"@sveltejs/kit": "^2.0.6",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@testing-library/jest-dom": "^6.1.5",
"@testing-library/svelte": "^4.0.3",
"@types/dom-to-image": "^2.6.4",
@@ -51,11 +50,11 @@
"identity-obj-proxy": "^3.0.0",
"postcss": "^8.4.21",
"prettier": "^3.1.0",
"prettier-plugin-organize-imports": "^3.2.4",
"prettier-plugin-svelte": "^3.1.2",
"rollup-plugin-visualizer": "^5.12.0",
"svelte": "^4.2.11",
"svelte-check": "^3.6.4",
"svelte": "^4.0.5",
"svelte-check": "^3.4.3",
"svelte-preprocess": "^5.0.3",
"tailwindcss": "^3.2.7",
"tslib": "^2.5.0",
"typescript": "^5.3.3",
@@ -1089,13 +1088,13 @@
}
},
"node_modules/@maplibre/maplibre-gl-style-spec": {
"version": "20.1.1",
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.1.1.tgz",
"integrity": "sha512-z85ARNPCBI2Cs5cPOS3DSbraTN+ue8zrcYVoSWBuNrD/mA+2SKAJ+hIzI22uN7gac6jBMnCdpPKRxS/V0KSZVQ==",
"version": "19.3.3",
"resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-19.3.3.tgz",
"integrity": "sha512-cOZZOVhDSulgK0meTsTkmNXb1ahVvmTmWmfx9gRBwc6hq98wS9JP35ESIoNq3xqEan+UN+gn8187Z6E4NKhLsw==",
"dependencies": {
"@mapbox/jsonlint-lines-primitives": "~2.0.2",
"@mapbox/unitbezier": "^0.0.1",
"json-stringify-pretty-compact": "^4.0.0",
"json-stringify-pretty-compact": "^3.0.0",
"minimist": "^1.2.8",
"rw": "^1.3.3",
"sort-object": "^3.0.3"
@@ -1684,17 +1683,9 @@
"integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw=="
},
"node_modules/@types/geojson": {
"version": "7946.0.14",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz",
"integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg=="
},
"node_modules/@types/geojson-vt": {
"version": "3.2.5",
"resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz",
"integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==",
"dependencies": {
"@types/geojson": "*"
}
"version": "7946.0.13",
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.13.tgz",
"integrity": "sha512-bmrNrgKMOhM3WsafmbGmC+6dsF2Z308vLFsQ3a/bT8X8Sv5clVYpPars/UPq+sAaJP+5OoLAYgwbkS5QEJdLUQ=="
},
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.6",
@@ -1714,14 +1705,6 @@
"integrity": "sha512-q2ybP0u0NVj87oMnGZOGxY2iUN8ddr48zPOBHBdbOLpsMTA/keGj+93ou+OMCnJk0xewzlNIaVEkxM6VBD3E2w==",
"dev": true
},
"node_modules/@types/leaflet": {
"version": "1.9.8",
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.8.tgz",
"integrity": "sha512-EXdsL4EhoUtGm2GC2ZYtXn+Fzc6pluVgagvo2VC1RHWToLGlTRwVYoDpqS/7QXa01rmDyBjJk3Catpf60VMkwg==",
"dependencies": {
"@types/geojson": "*"
}
},
"node_modules/@types/lodash": {
"version": "4.14.199",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.199.tgz",
@@ -4042,9 +4025,9 @@
}
},
"node_modules/fflate": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
"integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A=="
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.1.tgz",
"integrity": "sha512-/exOvEuc+/iaUm105QIiOt4LpBdMTWsXxqR0HDF35vx3fmaKzw7354gTilCh5rkzEt8WYyG//ku3h3nRmd7CHQ=="
},
"node_modules/file-entry-cache": {
"version": "6.0.1",
@@ -5291,9 +5274,9 @@
"dev": true
},
"node_modules/json-stringify-pretty-compact": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz",
"integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q=="
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz",
"integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA=="
},
"node_modules/json5": {
"version": "2.2.3",
@@ -5556,9 +5539,9 @@
"dev": true
},
"node_modules/maplibre-gl": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.0.1.tgz",
"integrity": "sha512-UF+wI2utIciFXNg6+gYaMe7IGa9fMLzAZM3vdlGilqyWYmuibjcN40yGVgkz2r28//aOLphvtli3TbDEjEqHww==",
"version": "3.6.2",
"resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-3.6.2.tgz",
"integrity": "sha512-krg2KFIdOpLPngONDhP6ixCoWl5kbdMINP0moMSJFVX7wX1Clm2M9hlNKXS8vBGlVWwR5R3ZfI6IPrYz7c+aCQ==",
"dependencies": {
"@mapbox/geojson-rewind": "^0.5.2",
"@mapbox/jsonlint-lines-primitives": "^2.0.2",
@@ -5567,9 +5550,8 @@
"@mapbox/unitbezier": "^0.0.1",
"@mapbox/vector-tile": "^1.3.1",
"@mapbox/whoots-js": "^3.1.0",
"@maplibre/maplibre-gl-style-spec": "^20.1.1",
"@types/geojson": "^7946.0.14",
"@types/geojson-vt": "3.2.5",
"@maplibre/maplibre-gl-style-spec": "^19.3.3",
"@types/geojson": "^7946.0.13",
"@types/mapbox__point-geometry": "^0.1.4",
"@types/mapbox__vector-tile": "^1.3.4",
"@types/pbf": "^3.0.5",
@@ -6207,11 +6189,10 @@
}
},
"node_modules/pmtiles": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/pmtiles/-/pmtiles-3.0.3.tgz",
"integrity": "sha512-tj4l3HHJd6/qf9VefzlPK2eYEQgbf+4uXPzNlrj3k7hHvLtibYSxfp51TF6ALt4YezM8MCdiOminnHvdAyqyGg==",
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/pmtiles/-/pmtiles-2.11.0.tgz",
"integrity": "sha512-dU9SzzaqmCGpdEuTnIba6bDHT6j09ZJFIXxwGpvkiEnce3ZnBB1VKt6+EOmJGueriweaZLAMTUmKVElU2CBe0g==",
"dependencies": {
"@types/leaflet": "^1.9.8",
"fflate": "^0.8.0"
}
},
@@ -6417,26 +6398,6 @@
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/prettier-plugin-organize-imports": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.4.tgz",
"integrity": "sha512-6m8WBhIp0dfwu0SkgfOxJqh+HpdyfqSSLfKKRZSFbDuEQXDDndb8fTpRWkUrX/uBenkex3MgnVk0J3b3Y5byog==",
"dev": true,
"peerDependencies": {
"@volar/vue-language-plugin-pug": "^1.0.4",
"@volar/vue-typescript": "^1.0.4",
"prettier": ">=2.0",
"typescript": ">=2.9"
},
"peerDependenciesMeta": {
"@volar/vue-language-plugin-pug": {
"optional": true
},
"@volar/vue-typescript": {
"optional": true
}
}
},
"node_modules/prettier-plugin-svelte": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.1.2.tgz",
@@ -7427,9 +7388,9 @@
}
},
"node_modules/svelte": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.11.tgz",
"integrity": "sha512-YIQk3J4X89wOLhjsqIW8tqY3JHPuBdtdOIkASP2PZeAMcSW9RsIjQzMesCrxOF3gdWYC0mKknlKF7OqmLM+Zqg==",
"version": "4.2.10",
"resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.10.tgz",
"integrity": "sha512-Ep06yCaCdgG1Mafb/Rx8sJ1QS3RW2I2BxGp2Ui9LBHSZ2/tO/aGLc5WqPjgiAP6KAnLJGaIr/zzwQlOo1b8MxA==",
"dependencies": {
"@ampproject/remapping": "^2.2.1",
"@jridgewell/sourcemap-codec": "^1.4.15",
@@ -7523,15 +7484,15 @@
}
},
"node_modules/svelte-maplibre": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/svelte-maplibre/-/svelte-maplibre-0.8.0.tgz",
"integrity": "sha512-sRSj/zQa7LTfHNIzKcYe+sa9qHClt/OAXcdPQ0w3ksLbCMmVHGk4B2yIXHCVk0g4sc18M85N8KGsHVtZoNC+Mw==",
"version": "0.7.7",
"resolved": "https://registry.npmjs.org/svelte-maplibre/-/svelte-maplibre-0.7.7.tgz",
"integrity": "sha512-fnv8L3tA4EMePp9BGKAc8AvXCsg34z56NBMGjYkz6qkl90qSTY4vUhIu1KXbwjGfQmHBmPkIl9VSdnnHCMnaRA==",
"dependencies": {
"d3-geo": "^3.1.0",
"just-compare": "^2.3.0",
"just-flush": "^2.3.0",
"maplibre-gl": "^4.0.0",
"pmtiles": "^3.0.3"
"maplibre-gl": "^3.5.0",
"pmtiles": "^2.10.0"
},
"peerDependencies": {
"@deck.gl/core": "^8.8.0",
+6 -7
View File
@@ -24,10 +24,9 @@
"devDependencies": {
"@faker-js/faker": "^8.0.0",
"@floating-ui/dom": "^1.5.1",
"@socket.io/component-emitter": "^3.1.0",
"@sveltejs/adapter-static": "^3.0.1",
"@sveltejs/kit": "^2.5.0",
"@sveltejs/vite-plugin-svelte": "^3.0.2",
"@sveltejs/kit": "^2.0.6",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@testing-library/jest-dom": "^6.1.5",
"@testing-library/svelte": "^4.0.3",
"@types/dom-to-image": "^2.6.4",
@@ -46,11 +45,11 @@
"identity-obj-proxy": "^3.0.0",
"postcss": "^8.4.21",
"prettier": "^3.1.0",
"prettier-plugin-organize-imports": "^3.2.4",
"prettier-plugin-svelte": "^3.1.2",
"rollup-plugin-visualizer": "^5.12.0",
"svelte": "^4.2.11",
"svelte-check": "^3.6.4",
"svelte": "^4.0.5",
"svelte-check": "^3.4.3",
"svelte-preprocess": "^5.0.3",
"tailwindcss": "^3.2.7",
"tslib": "^2.5.0",
"typescript": "^5.3.3",
@@ -73,7 +72,7 @@
"luxon": "^3.2.1",
"socket.io-client": "^4.6.1",
"svelte-local-storage-store": "^0.6.0",
"svelte-maplibre": "^0.8.0",
"svelte-maplibre": "^0.7.0",
"thumbhash": "^0.1.1"
}
}
+178
View File
@@ -0,0 +1,178 @@
import {
APIKeyApi,
ActivityApi,
AlbumApi,
AssetApi,
AssetApiFp,
AssetJobName,
AuditApi,
AuthenticationApi,
DownloadApi,
FaceApi,
JobApi,
JobName,
LibraryApi,
OAuthApi,
PartnerApi,
PersonApi,
SearchApi,
ServerInfoApi,
SharedLinkApi,
SystemConfigApi,
TrashApi,
UserApi,
UserApiFp,
base,
common,
configuration,
} from '@immich/sdk/axios';
import type { ApiParams as ApiParameters } from './types';
class ImmichApi {
public activityApi: ActivityApi;
public albumApi: AlbumApi;
public downloadApi: DownloadApi;
public libraryApi: LibraryApi;
public assetApi: AssetApi;
public auditApi: AuditApi;
public authenticationApi: AuthenticationApi;
public faceApi: FaceApi;
public jobApi: JobApi;
public keyApi: APIKeyApi;
public oauthApi: OAuthApi;
public partnerApi: PartnerApi;
public searchApi: SearchApi;
public serverInfoApi: ServerInfoApi;
public sharedLinkApi: SharedLinkApi;
public personApi: PersonApi;
public systemConfigApi: SystemConfigApi;
public userApi: UserApi;
public trashApi: TrashApi;
private config: configuration.Configuration;
private key?: string;
get isSharedLink() {
return !!this.key;
}
constructor(parameters: configuration.ConfigurationParameters) {
this.config = new configuration.Configuration(parameters);
this.activityApi = new ActivityApi(this.config);
this.albumApi = new AlbumApi(this.config);
this.auditApi = new AuditApi(this.config);
this.downloadApi = new DownloadApi(this.config);
this.libraryApi = new LibraryApi(this.config);
this.assetApi = new AssetApi(this.config);
this.authenticationApi = new AuthenticationApi(this.config);
this.faceApi = new FaceApi(this.config);
this.jobApi = new JobApi(this.config);
this.keyApi = new APIKeyApi(this.config);
this.oauthApi = new OAuthApi(this.config);
this.partnerApi = new PartnerApi(this.config);
this.searchApi = new SearchApi(this.config);
this.serverInfoApi = new ServerInfoApi(this.config);
this.sharedLinkApi = new SharedLinkApi(this.config);
this.personApi = new PersonApi(this.config);
this.systemConfigApi = new SystemConfigApi(this.config);
this.userApi = new UserApi(this.config);
this.trashApi = new TrashApi(this.config);
}
private createUrl(path: string, parameters?: Record<string, unknown>) {
const searchParameters = new URLSearchParams();
for (const key in parameters) {
const value = parameters[key];
if (value !== undefined && value !== null) {
searchParameters.set(key, value.toString());
}
}
const url = new URL(path, common.DUMMY_BASE_URL);
url.search = searchParameters.toString();
return (this.config.basePath || base.BASE_PATH) + common.toPathString(url);
}
public setKey(key: string) {
this.key = key;
}
public getKey(): string | undefined {
return this.key;
}
public setAccessToken(accessToken: string) {
this.config.accessToken = accessToken;
}
public removeAccessToken() {
this.config.accessToken = undefined;
}
public setBaseUrl(baseUrl: string) {
this.config.basePath = baseUrl;
}
public getAssetFileUrl(...[assetId, isThumb, isWeb]: ApiParameters<typeof AssetApiFp, 'serveFile'>) {
const path = `/asset/file/${assetId}`;
return this.createUrl(path, { isThumb, isWeb, key: this.getKey() });
}
public getAssetThumbnailUrl(...[assetId, format]: ApiParameters<typeof AssetApiFp, 'getAssetThumbnail'>) {
const path = `/asset/thumbnail/${assetId}`;
return this.createUrl(path, { format, key: this.getKey() });
}
public getProfileImageUrl(...[userId]: ApiParameters<typeof UserApiFp, 'getProfileImage'>) {
const path = `/user/profile-image/${userId}`;
return this.createUrl(path);
}
public getPeopleThumbnailUrl(personId: string) {
const path = `/person/${personId}/thumbnail`;
return this.createUrl(path);
}
public getJobName(jobName: JobName) {
const names: Record<JobName, string> = {
[JobName.ThumbnailGeneration]: 'Generate Thumbnails',
[JobName.MetadataExtraction]: 'Extract Metadata',
[JobName.Sidecar]: 'Sidecar Metadata',
[JobName.SmartSearch]: 'Smart Search',
[JobName.FaceDetection]: 'Face Detection',
[JobName.FacialRecognition]: 'Facial Recognition',
[JobName.VideoConversion]: 'Transcode Videos',
[JobName.StorageTemplateMigration]: 'Storage Template Migration',
[JobName.Migration]: 'Migration',
[JobName.BackgroundTask]: 'Background Tasks',
[JobName.Search]: 'Search',
[JobName.Library]: 'Library',
};
return names[jobName];
}
public getAssetJobName(job: AssetJobName) {
const names: Record<AssetJobName, string> = {
[AssetJobName.RefreshMetadata]: 'Refresh metadata',
[AssetJobName.RegenerateThumbnail]: 'Refresh thumbnails',
[AssetJobName.TranscodeVideo]: 'Refresh encoded videos',
};
return names[job];
}
public getAssetJobMessage(job: AssetJobName) {
const messages: Record<AssetJobName, string> = {
[AssetJobName.RefreshMetadata]: 'Refreshing metadata',
[AssetJobName.RegenerateThumbnail]: `Regenerating thumbnails`,
[AssetJobName.TranscodeVideo]: `Refreshing encoded video`,
};
return messages[job];
}
}
export const api = new ImmichApi({ basePath: '/api' });
+3
View File
@@ -0,0 +1,3 @@
export * from './api';
export * from '@immich/sdk/axios';
export * from './utils';
+8
View File
@@ -0,0 +1,8 @@
import type { Configuration } from '@immich/sdk/axios';
/* eslint-disable @typescript-eslint/no-explicit-any */
export type ApiFp = (configuration: Configuration) => Record<any, (...arguments_: any) => any>;
export type OmitLast<T extends readonly unknown[]> = T extends readonly [...infer U, any?] ? U : [...T];
export type ApiParams<F extends ApiFp, K extends keyof ReturnType<F>> = OmitLast<Parameters<ReturnType<F>[K]>>;
+63
View File
@@ -0,0 +1,63 @@
import type { AxiosError, AxiosPromise } from 'axios';
import {
notificationController,
NotificationType,
} from '../lib/components/shared-components/notification/notification';
import { handleError } from '../lib/utils/handle-error';
import { api } from './api';
import type { UserResponseDto } from '@immich/sdk/axios';
export type ApiError = AxiosError<{ message: string }>;
export const copyToClipboard = async (secret: string) => {
try {
await navigator.clipboard.writeText(secret);
notificationController.show({ message: 'Copied to clipboard!', type: NotificationType.Info });
} catch (error) {
handleError(error, 'Cannot copy to clipboard, make sure you are accessing the page through https');
}
};
export const makeSharedLinkUrl = (externalDomain: string, key: string) => {
let url = externalDomain || window.location.origin;
if (!url.endsWith('/')) {
url += '/';
}
return `${url}share/${key}`;
};
export const oauth = {
isCallback: (location: Location) => {
const search = location.search;
return search.includes('code=') || search.includes('error=');
},
isAutoLaunchDisabled: (location: Location) => {
const values = ['autoLaunch=0', 'password=1', 'password=true'];
for (const value of values) {
if (location.search.includes(value)) {
return true;
}
}
return false;
},
authorize: async (location: Location) => {
try {
const redirectUri = location.href.split('?')[0];
const { data } = await api.oauthApi.startOAuth({ oAuthConfigDto: { redirectUri } });
window.location.href = data.url;
return true;
} catch (error) {
handleError(error, 'Unable to login with OAuth');
return false;
}
},
login: (location: Location) => {
return api.oauthApi.finishOAuth({ oAuthCallbackDto: { url: location.href } });
},
link: (location: Location): AxiosPromise<UserResponseDto> => {
return api.oauthApi.linkOAuthAccount({ oAuthCallbackDto: { url: location.href } });
},
unlink: () => {
return api.oauthApi.unlinkOAuthAccount();
},
};
-14
View File
@@ -1,14 +0,0 @@
import { AssetApi, DownloadApi, configuration } from '@immich/sdk/axios';
class ImmichApi {
public downloadApi: DownloadApi;
public assetApi: AssetApi;
constructor(parameters: configuration.ConfigurationParameters) {
const config = new configuration.Configuration(parameters);
this.downloadApi = new DownloadApi(config);
this.assetApi = new AssetApi(config);
}
}
export const api = new ImmichApi({ basePath: '/api' });
@@ -1,8 +1,8 @@
<script lang="ts">
import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
import { handleError } from '$lib/utils/handle-error';
import { deleteUser, type UserResponseDto } from '@immich/sdk';
import { api, type UserResponseDto } from '@api';
import { createEventDispatcher } from 'svelte';
import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
import { handleError } from '../../utils/handle-error';
export let user: UserResponseDto;
@@ -11,10 +11,10 @@
fail: void;
}>();
const handleDeleteUser = async () => {
const deleteUser = async () => {
try {
const { deletedAt } = await deleteUser({ id: user.id });
if (deletedAt == undefined) {
const deletedUser = await api.userApi.deleteUser({ id: user.id });
if (deletedUser.data.deletedAt == undefined) {
dispatch('fail');
} else {
dispatch('success');
@@ -26,7 +26,7 @@
};
</script>
<ConfirmDialogue title="Delete User" confirmText="Delete" on:confirm={handleDeleteUser} on:cancel>
<ConfirmDialogue title="Delete User" confirmText="Delete" on:confirm={deleteUser} on:cancel>
<svelte:fragment slot="prompt">
<div class="flex flex-col gap-4">
<p>
@@ -1,9 +1,12 @@
<script lang="ts">
import { locale } from '$lib/stores/preferences.store';
import { createEventDispatcher } from 'svelte';
import { JobCommand, type JobCommandDto, type JobCountsDto, type QueueStatusDto } from '@api';
import Badge from '$lib/components/elements/badge.svelte';
import JobTileButton from './job-tile-button.svelte';
import JobTileStatus from './job-tile-status.svelte';
import Button from '$lib/components/elements/buttons/button.svelte';
import Icon from '$lib/components/elements/icon.svelte';
import { locale } from '$lib/stores/preferences.store';
import { JobCommand, type JobCommandDto, type JobCountsDto, type QueueStatusDto } from '@immich/sdk';
import {
mdiAlertCircle,
mdiAllInclusive,
@@ -13,9 +16,6 @@
mdiPlay,
mdiSelectionSearch,
} from '@mdi/js';
import { createEventDispatcher } from 'svelte';
import JobTileButton from './job-tile-button.svelte';
import JobTileStatus from './job-tile-status.svelte';
export let title: string;
export let subtitle: string | undefined;
@@ -4,9 +4,9 @@
NotificationType,
} from '$lib/components/shared-components/notification/notification';
import { featureFlags } from '$lib/stores/server-config.store';
import { getJobName } from '$lib/utils';
import { handleError } from '$lib/utils/handle-error';
import { JobCommand, JobName, sendJobCommand, type AllJobStatusResponseDto, type JobCommandDto } from '@immich/sdk';
import { type AllJobStatusResponseDto, api, JobCommand, type JobCommandDto, JobName } from '@api';
import type { ComponentType } from 'svelte';
import {
mdiFaceRecognition,
mdiFileJpgBox,
@@ -18,7 +18,6 @@
mdiTagFaces,
mdiVideo,
} from '@mdi/js';
import type { ComponentType } from 'svelte';
import ConfirmDialogue from '../../shared-components/confirm-dialogue.svelte';
import JobTile from './job-tile.svelte';
import StorageMigrationDescription from './storage-migration-description.svelte';
@@ -59,23 +58,23 @@
$: jobDetails = <Partial<Record<JobName, JobDetails>>>{
[JobName.ThumbnailGeneration]: {
icon: mdiFileJpgBox,
title: getJobName(JobName.ThumbnailGeneration),
title: api.getJobName(JobName.ThumbnailGeneration),
subtitle: 'Generate large, small and blurred thumbnails for each asset, as well as thumbnails for each person',
},
[JobName.MetadataExtraction]: {
icon: mdiTable,
title: getJobName(JobName.MetadataExtraction),
title: api.getJobName(JobName.MetadataExtraction),
subtitle: 'Extract metadata information from each asset, such as GPS and resolution',
},
[JobName.Library]: {
icon: mdiLibraryShelves,
title: getJobName(JobName.Library),
title: api.getJobName(JobName.Library),
subtitle: 'Perform library tasks',
allText: 'ALL',
missingText: 'REFRESH',
},
[JobName.Sidecar]: {
title: getJobName(JobName.Sidecar),
title: api.getJobName(JobName.Sidecar),
icon: mdiFileXmlBox,
subtitle: 'Discover or synchronize sidecar metadata from the filesystem',
allText: 'SYNC',
@@ -84,13 +83,13 @@
},
[JobName.SmartSearch]: {
icon: mdiImageSearch,
title: getJobName(JobName.SmartSearch),
title: api.getJobName(JobName.SmartSearch),
subtitle: 'Run machine learning on assets to support smart search',
disabled: !$featureFlags.smartSearch,
},
[JobName.FaceDetection]: {
icon: mdiFaceRecognition,
title: getJobName(JobName.FaceDetection),
title: api.getJobName(JobName.FaceDetection),
subtitle:
'Detect the faces in assets using machine learning. For videos, only the thumbnail is considered. "All" (re-)processes all assets. "Missing" queues assets that haven\'t been processed yet. Detected faces will be queued for Facial Recognition after Face Detection is complete, grouping them into existing or new people.',
handleCommand: handleConfirmCommand,
@@ -98,7 +97,7 @@
},
[JobName.FacialRecognition]: {
icon: mdiTagFaces,
title: getJobName(JobName.FacialRecognition),
title: api.getJobName(JobName.FacialRecognition),
subtitle:
'Group detected faces into people. This step runs after Face Detection is complete. "All" (re-)clusters all faces. "Missing" queues faces that don\'t have a person assigned.',
handleCommand: handleConfirmCommand,
@@ -106,18 +105,18 @@
},
[JobName.VideoConversion]: {
icon: mdiVideo,
title: getJobName(JobName.VideoConversion),
title: api.getJobName(JobName.VideoConversion),
subtitle: 'Transcode videos for wider compatibility with browsers and devices',
},
[JobName.StorageTemplateMigration]: {
icon: mdiFolderMove,
title: getJobName(JobName.StorageTemplateMigration),
title: api.getJobName(JobName.StorageTemplateMigration),
allowForceCommand: false,
component: StorageMigrationDescription,
},
[JobName.Migration]: {
icon: mdiFolderMove,
title: getJobName(JobName.Migration),
title: api.getJobName(JobName.Migration),
subtitle: 'Migrate thumbnails for assets and faces to the latest folder structure',
allowForceCommand: false,
},
@@ -128,7 +127,8 @@
const title = jobDetails[jobId]?.title;
try {
jobs[jobId] = await sendJobCommand({ id: jobId, jobCommandDto: jobCommand });
const { data } = await api.jobApi.sendJobCommand({ id: jobId, jobCommandDto: jobCommand });
jobs[jobId] = data;
switch (jobCommand.command) {
case JobCommand.Empty: {
@@ -1,7 +1,7 @@
<script lang="ts">
import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
import { restoreUser, type UserResponseDto } from '@immich/sdk';
import { api, type UserResponseDto } from '@api';
import { createEventDispatcher } from 'svelte';
import ConfirmDialogue from '$lib/components/shared-components/confirm-dialogue.svelte';
export let user: UserResponseDto;
@@ -10,9 +10,9 @@
fail: void;
}>();
const handleRestoreUser = async () => {
const { deletedAt } = await restoreUser({ id: user.id });
if (deletedAt == undefined) {
const restoreUser = async () => {
const restoredUser = await api.userApi.restoreUser({ id: user.id });
if (restoredUser.data.deletedAt == undefined) {
dispatch('success');
} else {
dispatch('fail');
@@ -20,13 +20,7 @@
};
</script>
<ConfirmDialogue
title="Restore User"
confirmText="Continue"
confirmColor="green"
on:confirm={handleRestoreUser}
on:cancel
>
<ConfirmDialogue title="Restore User" confirmText="Continue" confirmColor="green" on:confirm={restoreUser} on:cancel>
<svelte:fragment slot="prompt">
<p><b>{user.name}</b>'s account will be restored.</p>
</svelte:fragment>
@@ -1,10 +1,10 @@
<script lang="ts">
import Icon from '$lib/components/elements/icon.svelte';
import { locale } from '$lib/stores/preferences.store';
import type { ServerStatsResponseDto } from '@api';
import { asByteUnitString, getBytesWithUnit } from '$lib/utils/byte-units';
import type { ServerStatsResponseDto } from '@immich/sdk';
import { mdiCameraIris, mdiChartPie, mdiPlayCircle } from '@mdi/js';
import StatsCard from './stats-card.svelte';
import { mdiCameraIris, mdiChartPie, mdiPlayCircle } from '@mdi/js';
import Icon from '$lib/components/elements/icon.svelte';
export let stats: ServerStatsResponseDto = {
photos: 0,
@@ -1,15 +1,15 @@
<svelte:options accessors />
<script lang="ts">
import { type SystemConfigDto, api } from '@api';
import {
NotificationType,
notificationController,
NotificationType,
} from '$lib/components/shared-components/notification/notification';
import { handleError } from '$lib/utils/handle-error';
import { getConfig, getConfigDefaults, updateConfig, type SystemConfigDto } from '@immich/sdk';
import { cloneDeep } from 'lodash-es';
import { createEventDispatcher, onMount } from 'svelte';
import type { SettingsEventType } from './admin-settings';
import { createEventDispatcher, onMount } from 'svelte';
import { cloneDeep } from 'lodash-es';
export let config: SystemConfigDto;
@@ -24,7 +24,7 @@
const handleSave = async (update: Partial<SystemConfigDto>) => {
try {
const newConfig = await updateConfig({
const { data: newConfig } = await api.systemConfigApi.updateConfig({
systemConfigDto: {
...savedConfig,
...update,
@@ -42,7 +42,7 @@
};
const reset = async (configKeys: Array<keyof SystemConfigDto>) => {
const resetConfig = await getConfig();
const { data: resetConfig } = await api.systemConfigApi.getConfig();
for (const key of configKeys) {
config = { ...config, [key]: resetConfig[key] };
@@ -66,7 +66,10 @@
};
onMount(async () => {
[savedConfig, defaultConfig] = await Promise.all([getConfig(), getConfigDefaults()]);
[savedConfig, defaultConfig] = await Promise.all([
api.systemConfigApi.getConfig().then((res) => res.data),
api.systemConfigApi.getConfigDefaults().then((res) => res.data),
]);
});
</script>
@@ -1,5 +1,5 @@
import type { ResetOptions } from '$lib/utils/dipatch';
import type { SystemConfigDto } from '@immich/sdk';
import type { SystemConfigDto } from '@api';
export type SettingsEventType = {
reset: ResetOptions & { configKeys: Array<keyof SystemConfigDto> };
@@ -1,25 +1,25 @@
<script lang="ts">
import Icon from '$lib/components/elements/icon.svelte';
import {
AudioCodec,
CQMode,
type SystemConfigDto,
ToneMapping,
TranscodeHWAccel,
TranscodePolicy,
VideoCodec,
type SystemConfigDto,
} from '@immich/sdk';
import { mdiHelpCircleOutline } from '@mdi/js';
import { isEqual, sortBy } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
import type { SettingsEventType } from '../admin-settings';
import SettingAccordion from '../setting-accordion.svelte';
} from '@api';
import SettingButtonsRow from '../setting-buttons-row.svelte';
import SettingCheckboxes from '../setting-checkboxes.svelte';
import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
import SettingSelect from '../setting-select.svelte';
import SettingSwitch from '../setting-switch.svelte';
import SettingCheckboxes from '../setting-checkboxes.svelte';
import { isEqual, sortBy } from 'lodash-es';
import { fade } from 'svelte/transition';
import SettingAccordion from '../setting-accordion.svelte';
import { mdiHelpCircleOutline } from '@mdi/js';
import Icon from '$lib/components/elements/icon.svelte';
import { createEventDispatcher } from 'svelte';
import type { SettingsEventType } from '../admin-settings';
export let savedConfig: SystemConfigDto;
export let defaultConfig: SystemConfigDto;
@@ -1,6 +1,5 @@
<script lang="ts">
import { getJobName } from '$lib/utils';
import { JobName, type SystemConfigDto, type SystemConfigJobDto } from '@immich/sdk';
import { api, JobName, type SystemConfigDto, type SystemConfigJobDto } from '@api';
import { isEqual } from 'lodash-es';
import { createEventDispatcher } from 'svelte';
import { fade } from 'svelte/transition';
@@ -28,8 +27,7 @@
JobName.Migration,
];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function isSystemConfigJobDto(jobName: any): jobName is keyof SystemConfigJobDto {
function isSystemConfigJobDto(jobName: JobName): jobName is keyof SystemConfigJobDto {
return jobName in config.job;
}
</script>
@@ -43,7 +41,7 @@
<SettingInputField
inputType={SettingInputFieldType.NUMBER}
{disabled}
label="{getJobName(jobName)} Concurrency"
label="{api.getJobName(jobName)} Concurrency"
desc=""
bind:value={config.job[jobName].concurrency}
required={true}
@@ -52,7 +50,7 @@
{:else}
<SettingInputField
inputType={SettingInputFieldType.NUMBER}
label="{getJobName(jobName)} Concurrency"
label="{api.getJobName(jobName)} Concurrency"
desc=""
value="1"
disabled={true}

Some files were not shown because too many files have changed in this diff Show More