feat: queues (#24142)

pull/24178/head
Jason Rasmussen 2025-11-25 08:19:40 -05:00 committed by GitHub
parent 66ae07ee39
commit 104fa09f69
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
37 changed files with 2487 additions and 336 deletions

View File

@ -12,7 +12,7 @@ import {
PersonCreateDto, PersonCreateDto,
QueueCommandDto, QueueCommandDto,
QueueName, QueueName,
QueuesResponseDto, QueuesResponseLegacyDto,
SharedLinkCreateDto, SharedLinkCreateDto,
UpdateLibraryDto, UpdateLibraryDto,
UserAdminCreateDto, UserAdminCreateDto,
@ -564,13 +564,13 @@ export const utils = {
await updateConfig({ systemConfigDto: defaultConfig }, { headers: asBearerAuth(accessToken) }); await updateConfig({ systemConfigDto: defaultConfig }, { headers: asBearerAuth(accessToken) });
}, },
isQueueEmpty: async (accessToken: string, queue: keyof QueuesResponseDto) => { isQueueEmpty: async (accessToken: string, queue: keyof QueuesResponseLegacyDto) => {
const queues = await getQueuesLegacy({ headers: asBearerAuth(accessToken) }); const queues = await getQueuesLegacy({ headers: asBearerAuth(accessToken) });
const jobCounts = queues[queue].jobCounts; const jobCounts = queues[queue].jobCounts;
return !jobCounts.active && !jobCounts.waiting; return !jobCounts.active && !jobCounts.waiting;
}, },
waitForQueueFinish: (accessToken: string, queue: keyof QueuesResponseDto, ms?: number) => { waitForQueueFinish: (accessToken: string, queue: keyof QueuesResponseLegacyDto, ms?: number) => {
// eslint-disable-next-line no-async-promise-executor // eslint-disable-next-line no-async-promise-executor
return new Promise<void>(async (resolve, reject) => { return new Promise<void>(async (resolve, reject) => {
const timeout = setTimeout(() => reject(new Error('Timed out waiting for queue to empty')), ms || 10_000); const timeout = setTimeout(() => reject(new Error('Timed out waiting for queue to empty')), ms || 10_000);

View File

@ -137,8 +137,10 @@ Class | Method | HTTP request | Description
*DeprecatedApi* | [**getAllUserAssetsByDeviceId**](doc//DeprecatedApi.md#getalluserassetsbydeviceid) | **GET** /assets/device/{deviceId} | Retrieve assets by device ID *DeprecatedApi* | [**getAllUserAssetsByDeviceId**](doc//DeprecatedApi.md#getalluserassetsbydeviceid) | **GET** /assets/device/{deviceId} | Retrieve assets by device ID
*DeprecatedApi* | [**getDeltaSync**](doc//DeprecatedApi.md#getdeltasync) | **POST** /sync/delta-sync | Get delta sync for user *DeprecatedApi* | [**getDeltaSync**](doc//DeprecatedApi.md#getdeltasync) | **POST** /sync/delta-sync | Get delta sync for user
*DeprecatedApi* | [**getFullSyncForUser**](doc//DeprecatedApi.md#getfullsyncforuser) | **POST** /sync/full-sync | Get full sync for user *DeprecatedApi* | [**getFullSyncForUser**](doc//DeprecatedApi.md#getfullsyncforuser) | **POST** /sync/full-sync | Get full sync for user
*DeprecatedApi* | [**getQueuesLegacy**](doc//DeprecatedApi.md#getqueueslegacy) | **GET** /jobs | Retrieve queue counts and status
*DeprecatedApi* | [**getRandom**](doc//DeprecatedApi.md#getrandom) | **GET** /assets/random | Get random assets *DeprecatedApi* | [**getRandom**](doc//DeprecatedApi.md#getrandom) | **GET** /assets/random | Get random assets
*DeprecatedApi* | [**replaceAsset**](doc//DeprecatedApi.md#replaceasset) | **PUT** /assets/{id}/original | Replace asset *DeprecatedApi* | [**replaceAsset**](doc//DeprecatedApi.md#replaceasset) | **PUT** /assets/{id}/original | Replace asset
*DeprecatedApi* | [**runQueueCommandLegacy**](doc//DeprecatedApi.md#runqueuecommandlegacy) | **PUT** /jobs/{name} | Run jobs
*DownloadApi* | [**downloadArchive**](doc//DownloadApi.md#downloadarchive) | **POST** /download/archive | Download asset archive *DownloadApi* | [**downloadArchive**](doc//DownloadApi.md#downloadarchive) | **POST** /download/archive | Download asset archive
*DownloadApi* | [**getDownloadInfo**](doc//DownloadApi.md#getdownloadinfo) | **POST** /download/info | Retrieve download information *DownloadApi* | [**getDownloadInfo**](doc//DownloadApi.md#getdownloadinfo) | **POST** /download/info | Retrieve download information
*DuplicatesApi* | [**deleteDuplicate**](doc//DuplicatesApi.md#deleteduplicate) | **DELETE** /duplicates/{id} | Delete a duplicate *DuplicatesApi* | [**deleteDuplicate**](doc//DuplicatesApi.md#deleteduplicate) | **DELETE** /duplicates/{id} | Delete a duplicate
@ -198,6 +200,11 @@ Class | Method | HTTP request | Description
*PeopleApi* | [**updatePerson**](doc//PeopleApi.md#updateperson) | **PUT** /people/{id} | Update person *PeopleApi* | [**updatePerson**](doc//PeopleApi.md#updateperson) | **PUT** /people/{id} | Update person
*PluginsApi* | [**getPlugin**](doc//PluginsApi.md#getplugin) | **GET** /plugins/{id} | Retrieve a plugin *PluginsApi* | [**getPlugin**](doc//PluginsApi.md#getplugin) | **GET** /plugins/{id} | Retrieve a plugin
*PluginsApi* | [**getPlugins**](doc//PluginsApi.md#getplugins) | **GET** /plugins | List all plugins *PluginsApi* | [**getPlugins**](doc//PluginsApi.md#getplugins) | **GET** /plugins | List all plugins
*QueuesApi* | [**emptyQueue**](doc//QueuesApi.md#emptyqueue) | **DELETE** /queues/{name}/jobs | Empty a queue
*QueuesApi* | [**getQueue**](doc//QueuesApi.md#getqueue) | **GET** /queues/{name} | Retrieve a queue
*QueuesApi* | [**getQueueJobs**](doc//QueuesApi.md#getqueuejobs) | **GET** /queues/{name}/jobs | Retrieve queue jobs
*QueuesApi* | [**getQueues**](doc//QueuesApi.md#getqueues) | **GET** /queues | List all queues
*QueuesApi* | [**updateQueue**](doc//QueuesApi.md#updatequeue) | **PUT** /queues/{name} | Update a queue
*SearchApi* | [**getAssetsByCity**](doc//SearchApi.md#getassetsbycity) | **GET** /search/cities | Retrieve assets by city *SearchApi* | [**getAssetsByCity**](doc//SearchApi.md#getassetsbycity) | **GET** /search/cities | Retrieve assets by city
*SearchApi* | [**getExploreData**](doc//SearchApi.md#getexploredata) | **GET** /search/explore | Retrieve explore data *SearchApi* | [**getExploreData**](doc//SearchApi.md#getexploredata) | **GET** /search/explore | Retrieve explore data
*SearchApi* | [**getSearchSuggestions**](doc//SearchApi.md#getsearchsuggestions) | **GET** /search/suggestions | Retrieve search suggestions *SearchApi* | [**getSearchSuggestions**](doc//SearchApi.md#getsearchsuggestions) | **GET** /search/suggestions | Retrieve search suggestions
@ -396,6 +403,7 @@ Class | Method | HTTP request | Description
- [FoldersUpdate](doc//FoldersUpdate.md) - [FoldersUpdate](doc//FoldersUpdate.md)
- [ImageFormat](doc//ImageFormat.md) - [ImageFormat](doc//ImageFormat.md)
- [JobCreateDto](doc//JobCreateDto.md) - [JobCreateDto](doc//JobCreateDto.md)
- [JobName](doc//JobName.md)
- [JobSettingsDto](doc//JobSettingsDto.md) - [JobSettingsDto](doc//JobSettingsDto.md)
- [LibraryResponseDto](doc//LibraryResponseDto.md) - [LibraryResponseDto](doc//LibraryResponseDto.md)
- [LibraryStatsResponseDto](doc//LibraryStatsResponseDto.md) - [LibraryStatsResponseDto](doc//LibraryStatsResponseDto.md)
@ -465,11 +473,16 @@ Class | Method | HTTP request | Description
- [PurchaseUpdate](doc//PurchaseUpdate.md) - [PurchaseUpdate](doc//PurchaseUpdate.md)
- [QueueCommand](doc//QueueCommand.md) - [QueueCommand](doc//QueueCommand.md)
- [QueueCommandDto](doc//QueueCommandDto.md) - [QueueCommandDto](doc//QueueCommandDto.md)
- [QueueDeleteDto](doc//QueueDeleteDto.md)
- [QueueJobResponseDto](doc//QueueJobResponseDto.md)
- [QueueJobStatus](doc//QueueJobStatus.md)
- [QueueName](doc//QueueName.md) - [QueueName](doc//QueueName.md)
- [QueueResponseDto](doc//QueueResponseDto.md) - [QueueResponseDto](doc//QueueResponseDto.md)
- [QueueResponseLegacyDto](doc//QueueResponseLegacyDto.md)
- [QueueStatisticsDto](doc//QueueStatisticsDto.md) - [QueueStatisticsDto](doc//QueueStatisticsDto.md)
- [QueueStatusDto](doc//QueueStatusDto.md) - [QueueStatusLegacyDto](doc//QueueStatusLegacyDto.md)
- [QueuesResponseDto](doc//QueuesResponseDto.md) - [QueueUpdateDto](doc//QueueUpdateDto.md)
- [QueuesResponseLegacyDto](doc//QueuesResponseLegacyDto.md)
- [RandomSearchDto](doc//RandomSearchDto.md) - [RandomSearchDto](doc//RandomSearchDto.md)
- [RatingsResponse](doc//RatingsResponse.md) - [RatingsResponse](doc//RatingsResponse.md)
- [RatingsUpdate](doc//RatingsUpdate.md) - [RatingsUpdate](doc//RatingsUpdate.md)

View File

@ -50,6 +50,7 @@ part 'api/notifications_admin_api.dart';
part 'api/partners_api.dart'; part 'api/partners_api.dart';
part 'api/people_api.dart'; part 'api/people_api.dart';
part 'api/plugins_api.dart'; part 'api/plugins_api.dart';
part 'api/queues_api.dart';
part 'api/search_api.dart'; part 'api/search_api.dart';
part 'api/server_api.dart'; part 'api/server_api.dart';
part 'api/sessions_api.dart'; part 'api/sessions_api.dart';
@ -154,6 +155,7 @@ part 'model/folders_response.dart';
part 'model/folders_update.dart'; part 'model/folders_update.dart';
part 'model/image_format.dart'; part 'model/image_format.dart';
part 'model/job_create_dto.dart'; part 'model/job_create_dto.dart';
part 'model/job_name.dart';
part 'model/job_settings_dto.dart'; part 'model/job_settings_dto.dart';
part 'model/library_response_dto.dart'; part 'model/library_response_dto.dart';
part 'model/library_stats_response_dto.dart'; part 'model/library_stats_response_dto.dart';
@ -223,11 +225,16 @@ part 'model/purchase_response.dart';
part 'model/purchase_update.dart'; part 'model/purchase_update.dart';
part 'model/queue_command.dart'; part 'model/queue_command.dart';
part 'model/queue_command_dto.dart'; part 'model/queue_command_dto.dart';
part 'model/queue_delete_dto.dart';
part 'model/queue_job_response_dto.dart';
part 'model/queue_job_status.dart';
part 'model/queue_name.dart'; part 'model/queue_name.dart';
part 'model/queue_response_dto.dart'; part 'model/queue_response_dto.dart';
part 'model/queue_response_legacy_dto.dart';
part 'model/queue_statistics_dto.dart'; part 'model/queue_statistics_dto.dart';
part 'model/queue_status_dto.dart'; part 'model/queue_status_legacy_dto.dart';
part 'model/queues_response_dto.dart'; part 'model/queue_update_dto.dart';
part 'model/queues_response_legacy_dto.dart';
part 'model/random_search_dto.dart'; part 'model/random_search_dto.dart';
part 'model/ratings_response.dart'; part 'model/ratings_response.dart';
part 'model/ratings_update.dart'; part 'model/ratings_update.dart';

View File

@ -248,6 +248,54 @@ class DeprecatedApi {
return null; return null;
} }
/// Retrieve queue counts and status
///
/// Retrieve the counts of the current queue, as well as the current status.
///
/// Note: This method returns the HTTP [Response].
Future<Response> getQueuesLegacyWithHttpInfo() async {
// ignore: prefer_const_declarations
final apiPath = r'/jobs';
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>[];
return apiClient.invokeAPI(
apiPath,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Retrieve queue counts and status
///
/// Retrieve the counts of the current queue, as well as the current status.
Future<QueuesResponseLegacyDto?> getQueuesLegacy() async {
final response = await getQueuesLegacyWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'QueuesResponseLegacyDto',) as QueuesResponseLegacyDto;
}
return null;
}
/// Get random assets /// Get random assets
/// ///
/// Retrieve a specified number of random assets for the authenticated user. /// Retrieve a specified number of random assets for the authenticated user.
@ -444,4 +492,65 @@ class DeprecatedApi {
} }
return null; return null;
} }
/// Run jobs
///
/// Queue all assets for a specific job type. Defaults to only queueing assets that have not yet been processed, but the force command can be used to re-process all assets.
///
/// Note: This method returns the HTTP [Response].
///
/// Parameters:
///
/// * [QueueName] name (required):
///
/// * [QueueCommandDto] queueCommandDto (required):
Future<Response> runQueueCommandLegacyWithHttpInfo(QueueName name, QueueCommandDto queueCommandDto,) async {
// ignore: prefer_const_declarations
final apiPath = r'/jobs/{name}'
.replaceAll('{name}', name.toString());
// ignore: prefer_final_locals
Object? postBody = queueCommandDto;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>['application/json'];
return apiClient.invokeAPI(
apiPath,
'PUT',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Run jobs
///
/// Queue all assets for a specific job type. Defaults to only queueing assets that have not yet been processed, but the force command can be used to re-process all assets.
///
/// Parameters:
///
/// * [QueueName] name (required):
///
/// * [QueueCommandDto] queueCommandDto (required):
Future<QueueResponseLegacyDto?> runQueueCommandLegacy(QueueName name, QueueCommandDto queueCommandDto,) async {
final response = await runQueueCommandLegacyWithHttpInfo(name, queueCommandDto,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'QueueResponseLegacyDto',) as QueueResponseLegacyDto;
}
return null;
}
} }

View File

@ -97,7 +97,7 @@ class JobsApi {
/// Retrieve queue counts and status /// Retrieve queue counts and status
/// ///
/// Retrieve the counts of the current queue, as well as the current status. /// Retrieve the counts of the current queue, as well as the current status.
Future<QueuesResponseDto?> getQueuesLegacy() async { Future<QueuesResponseLegacyDto?> getQueuesLegacy() async {
final response = await getQueuesLegacyWithHttpInfo(); final response = await getQueuesLegacyWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
@ -106,7 +106,7 @@ class JobsApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string. // FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'QueuesResponseDto',) as QueuesResponseDto; return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'QueuesResponseLegacyDto',) as QueuesResponseLegacyDto;
} }
return null; return null;
@ -158,7 +158,7 @@ class JobsApi {
/// * [QueueName] name (required): /// * [QueueName] name (required):
/// ///
/// * [QueueCommandDto] queueCommandDto (required): /// * [QueueCommandDto] queueCommandDto (required):
Future<QueueResponseDto?> runQueueCommandLegacy(QueueName name, QueueCommandDto queueCommandDto,) async { Future<QueueResponseLegacyDto?> runQueueCommandLegacy(QueueName name, QueueCommandDto queueCommandDto,) async {
final response = await runQueueCommandLegacyWithHttpInfo(name, queueCommandDto,); final response = await runQueueCommandLegacyWithHttpInfo(name, queueCommandDto,);
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
@ -167,7 +167,7 @@ class JobsApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string. // FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'QueueResponseDto',) as QueueResponseDto; return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'QueueResponseLegacyDto',) as QueueResponseLegacyDto;
} }
return null; return null;

308
mobile/openapi/lib/api/queues_api.dart generated Normal file
View File

@ -0,0 +1,308 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// 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 QueuesApi {
QueuesApi([ApiClient? apiClient]) : apiClient = apiClient ?? defaultApiClient;
final ApiClient apiClient;
/// Empty a queue
///
/// Removes all jobs from the specified queue.
///
/// Note: This method returns the HTTP [Response].
///
/// Parameters:
///
/// * [QueueName] name (required):
///
/// * [QueueDeleteDto] queueDeleteDto (required):
Future<Response> emptyQueueWithHttpInfo(QueueName name, QueueDeleteDto queueDeleteDto,) async {
// ignore: prefer_const_declarations
final apiPath = r'/queues/{name}/jobs'
.replaceAll('{name}', name.toString());
// ignore: prefer_final_locals
Object? postBody = queueDeleteDto;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>['application/json'];
return apiClient.invokeAPI(
apiPath,
'DELETE',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Empty a queue
///
/// Removes all jobs from the specified queue.
///
/// Parameters:
///
/// * [QueueName] name (required):
///
/// * [QueueDeleteDto] queueDeleteDto (required):
Future<void> emptyQueue(QueueName name, QueueDeleteDto queueDeleteDto,) async {
final response = await emptyQueueWithHttpInfo(name, queueDeleteDto,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
}
/// Retrieve a queue
///
/// Retrieves a specific queue by its name.
///
/// Note: This method returns the HTTP [Response].
///
/// Parameters:
///
/// * [QueueName] name (required):
Future<Response> getQueueWithHttpInfo(QueueName name,) async {
// ignore: prefer_const_declarations
final apiPath = r'/queues/{name}'
.replaceAll('{name}', name.toString());
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>[];
return apiClient.invokeAPI(
apiPath,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Retrieve a queue
///
/// Retrieves a specific queue by its name.
///
/// Parameters:
///
/// * [QueueName] name (required):
Future<QueueResponseDto?> getQueue(QueueName name,) async {
final response = await getQueueWithHttpInfo(name,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'QueueResponseDto',) as QueueResponseDto;
}
return null;
}
/// Retrieve queue jobs
///
/// Retrieves a list of queue jobs from the specified queue.
///
/// Note: This method returns the HTTP [Response].
///
/// Parameters:
///
/// * [QueueName] name (required):
///
/// * [List<QueueJobStatus>] status:
Future<Response> getQueueJobsWithHttpInfo(QueueName name, { List<QueueJobStatus>? status, }) async {
// ignore: prefer_const_declarations
final apiPath = r'/queues/{name}/jobs'
.replaceAll('{name}', name.toString());
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
if (status != null) {
queryParams.addAll(_queryParams('multi', 'status', status));
}
const contentTypes = <String>[];
return apiClient.invokeAPI(
apiPath,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Retrieve queue jobs
///
/// Retrieves a list of queue jobs from the specified queue.
///
/// Parameters:
///
/// * [QueueName] name (required):
///
/// * [List<QueueJobStatus>] status:
Future<List<QueueJobResponseDto>?> getQueueJobs(QueueName name, { List<QueueJobStatus>? status, }) async {
final response = await getQueueJobsWithHttpInfo(name, status: status, );
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
final responseBody = await _decodeBodyBytes(response);
return (await apiClient.deserializeAsync(responseBody, 'List<QueueJobResponseDto>') as List)
.cast<QueueJobResponseDto>()
.toList(growable: false);
}
return null;
}
/// List all queues
///
/// Retrieves a list of queues.
///
/// Note: This method returns the HTTP [Response].
Future<Response> getQueuesWithHttpInfo() async {
// ignore: prefer_const_declarations
final apiPath = r'/queues';
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>[];
return apiClient.invokeAPI(
apiPath,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// List all queues
///
/// Retrieves a list of queues.
Future<List<QueueResponseDto>?> getQueues() async {
final response = await getQueuesWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
final responseBody = await _decodeBodyBytes(response);
return (await apiClient.deserializeAsync(responseBody, 'List<QueueResponseDto>') as List)
.cast<QueueResponseDto>()
.toList(growable: false);
}
return null;
}
/// Update a queue
///
/// Change the paused status of a specific queue.
///
/// Note: This method returns the HTTP [Response].
///
/// Parameters:
///
/// * [QueueName] name (required):
///
/// * [QueueUpdateDto] queueUpdateDto (required):
Future<Response> updateQueueWithHttpInfo(QueueName name, QueueUpdateDto queueUpdateDto,) async {
// ignore: prefer_const_declarations
final apiPath = r'/queues/{name}'
.replaceAll('{name}', name.toString());
// ignore: prefer_final_locals
Object? postBody = queueUpdateDto;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>['application/json'];
return apiClient.invokeAPI(
apiPath,
'PUT',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Update a queue
///
/// Change the paused status of a specific queue.
///
/// Parameters:
///
/// * [QueueName] name (required):
///
/// * [QueueUpdateDto] queueUpdateDto (required):
Future<QueueResponseDto?> updateQueue(QueueName name, QueueUpdateDto queueUpdateDto,) async {
final response = await updateQueueWithHttpInfo(name, queueUpdateDto,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'QueueResponseDto',) as QueueResponseDto;
}
return null;
}
}

View File

@ -358,6 +358,8 @@ class ApiClient {
return ImageFormatTypeTransformer().decode(value); return ImageFormatTypeTransformer().decode(value);
case 'JobCreateDto': case 'JobCreateDto':
return JobCreateDto.fromJson(value); return JobCreateDto.fromJson(value);
case 'JobName':
return JobNameTypeTransformer().decode(value);
case 'JobSettingsDto': case 'JobSettingsDto':
return JobSettingsDto.fromJson(value); return JobSettingsDto.fromJson(value);
case 'LibraryResponseDto': case 'LibraryResponseDto':
@ -496,16 +498,26 @@ class ApiClient {
return QueueCommandTypeTransformer().decode(value); return QueueCommandTypeTransformer().decode(value);
case 'QueueCommandDto': case 'QueueCommandDto':
return QueueCommandDto.fromJson(value); return QueueCommandDto.fromJson(value);
case 'QueueDeleteDto':
return QueueDeleteDto.fromJson(value);
case 'QueueJobResponseDto':
return QueueJobResponseDto.fromJson(value);
case 'QueueJobStatus':
return QueueJobStatusTypeTransformer().decode(value);
case 'QueueName': case 'QueueName':
return QueueNameTypeTransformer().decode(value); return QueueNameTypeTransformer().decode(value);
case 'QueueResponseDto': case 'QueueResponseDto':
return QueueResponseDto.fromJson(value); return QueueResponseDto.fromJson(value);
case 'QueueResponseLegacyDto':
return QueueResponseLegacyDto.fromJson(value);
case 'QueueStatisticsDto': case 'QueueStatisticsDto':
return QueueStatisticsDto.fromJson(value); return QueueStatisticsDto.fromJson(value);
case 'QueueStatusDto': case 'QueueStatusLegacyDto':
return QueueStatusDto.fromJson(value); return QueueStatusLegacyDto.fromJson(value);
case 'QueuesResponseDto': case 'QueueUpdateDto':
return QueuesResponseDto.fromJson(value); return QueueUpdateDto.fromJson(value);
case 'QueuesResponseLegacyDto':
return QueuesResponseLegacyDto.fromJson(value);
case 'RandomSearchDto': case 'RandomSearchDto':
return RandomSearchDto.fromJson(value); return RandomSearchDto.fromJson(value);
case 'RatingsResponse': case 'RatingsResponse':

View File

@ -94,6 +94,9 @@ String parameterToString(dynamic value) {
if (value is ImageFormat) { if (value is ImageFormat) {
return ImageFormatTypeTransformer().encode(value).toString(); return ImageFormatTypeTransformer().encode(value).toString();
} }
if (value is JobName) {
return JobNameTypeTransformer().encode(value).toString();
}
if (value is LogLevel) { if (value is LogLevel) {
return LogLevelTypeTransformer().encode(value).toString(); return LogLevelTypeTransformer().encode(value).toString();
} }
@ -133,6 +136,9 @@ String parameterToString(dynamic value) {
if (value is QueueCommand) { if (value is QueueCommand) {
return QueueCommandTypeTransformer().encode(value).toString(); return QueueCommandTypeTransformer().encode(value).toString();
} }
if (value is QueueJobStatus) {
return QueueJobStatusTypeTransformer().encode(value).toString();
}
if (value is QueueName) { if (value is QueueName) {
return QueueNameTypeTransformer().encode(value).toString(); return QueueNameTypeTransformer().encode(value).toString();
} }

244
mobile/openapi/lib/model/job_name.dart generated Normal file
View File

@ -0,0 +1,244 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// 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 JobName {
/// Instantiate a new enum with the provided [value].
const JobName._(this.value);
/// The underlying value of this enum member.
final String value;
@override
String toString() => value;
String toJson() => value;
static const assetDelete = JobName._(r'AssetDelete');
static const assetDeleteCheck = JobName._(r'AssetDeleteCheck');
static const assetDetectFacesQueueAll = JobName._(r'AssetDetectFacesQueueAll');
static const assetDetectFaces = JobName._(r'AssetDetectFaces');
static const assetDetectDuplicatesQueueAll = JobName._(r'AssetDetectDuplicatesQueueAll');
static const assetDetectDuplicates = JobName._(r'AssetDetectDuplicates');
static const assetEncodeVideoQueueAll = JobName._(r'AssetEncodeVideoQueueAll');
static const assetEncodeVideo = JobName._(r'AssetEncodeVideo');
static const assetEmptyTrash = JobName._(r'AssetEmptyTrash');
static const assetExtractMetadataQueueAll = JobName._(r'AssetExtractMetadataQueueAll');
static const assetExtractMetadata = JobName._(r'AssetExtractMetadata');
static const assetFileMigration = JobName._(r'AssetFileMigration');
static const assetGenerateThumbnailsQueueAll = JobName._(r'AssetGenerateThumbnailsQueueAll');
static const assetGenerateThumbnails = JobName._(r'AssetGenerateThumbnails');
static const auditLogCleanup = JobName._(r'AuditLogCleanup');
static const auditTableCleanup = JobName._(r'AuditTableCleanup');
static const databaseBackup = JobName._(r'DatabaseBackup');
static const facialRecognitionQueueAll = JobName._(r'FacialRecognitionQueueAll');
static const facialRecognition = JobName._(r'FacialRecognition');
static const fileDelete = JobName._(r'FileDelete');
static const fileMigrationQueueAll = JobName._(r'FileMigrationQueueAll');
static const libraryDeleteCheck = JobName._(r'LibraryDeleteCheck');
static const libraryDelete = JobName._(r'LibraryDelete');
static const libraryRemoveAsset = JobName._(r'LibraryRemoveAsset');
static const libraryScanAssetsQueueAll = JobName._(r'LibraryScanAssetsQueueAll');
static const librarySyncAssets = JobName._(r'LibrarySyncAssets');
static const librarySyncFilesQueueAll = JobName._(r'LibrarySyncFilesQueueAll');
static const librarySyncFiles = JobName._(r'LibrarySyncFiles');
static const libraryScanQueueAll = JobName._(r'LibraryScanQueueAll');
static const memoryCleanup = JobName._(r'MemoryCleanup');
static const memoryGenerate = JobName._(r'MemoryGenerate');
static const notificationsCleanup = JobName._(r'NotificationsCleanup');
static const notifyUserSignup = JobName._(r'NotifyUserSignup');
static const notifyAlbumInvite = JobName._(r'NotifyAlbumInvite');
static const notifyAlbumUpdate = JobName._(r'NotifyAlbumUpdate');
static const userDelete = JobName._(r'UserDelete');
static const userDeleteCheck = JobName._(r'UserDeleteCheck');
static const userSyncUsage = JobName._(r'UserSyncUsage');
static const personCleanup = JobName._(r'PersonCleanup');
static const personFileMigration = JobName._(r'PersonFileMigration');
static const personGenerateThumbnail = JobName._(r'PersonGenerateThumbnail');
static const sessionCleanup = JobName._(r'SessionCleanup');
static const sendMail = JobName._(r'SendMail');
static const sidecarQueueAll = JobName._(r'SidecarQueueAll');
static const sidecarCheck = JobName._(r'SidecarCheck');
static const sidecarWrite = JobName._(r'SidecarWrite');
static const smartSearchQueueAll = JobName._(r'SmartSearchQueueAll');
static const smartSearch = JobName._(r'SmartSearch');
static const storageTemplateMigration = JobName._(r'StorageTemplateMigration');
static const storageTemplateMigrationSingle = JobName._(r'StorageTemplateMigrationSingle');
static const tagCleanup = JobName._(r'TagCleanup');
static const versionCheck = JobName._(r'VersionCheck');
static const ocrQueueAll = JobName._(r'OcrQueueAll');
static const ocr = JobName._(r'Ocr');
static const workflowRun = JobName._(r'WorkflowRun');
/// List of all possible values in this [enum][JobName].
static const values = <JobName>[
assetDelete,
assetDeleteCheck,
assetDetectFacesQueueAll,
assetDetectFaces,
assetDetectDuplicatesQueueAll,
assetDetectDuplicates,
assetEncodeVideoQueueAll,
assetEncodeVideo,
assetEmptyTrash,
assetExtractMetadataQueueAll,
assetExtractMetadata,
assetFileMigration,
assetGenerateThumbnailsQueueAll,
assetGenerateThumbnails,
auditLogCleanup,
auditTableCleanup,
databaseBackup,
facialRecognitionQueueAll,
facialRecognition,
fileDelete,
fileMigrationQueueAll,
libraryDeleteCheck,
libraryDelete,
libraryRemoveAsset,
libraryScanAssetsQueueAll,
librarySyncAssets,
librarySyncFilesQueueAll,
librarySyncFiles,
libraryScanQueueAll,
memoryCleanup,
memoryGenerate,
notificationsCleanup,
notifyUserSignup,
notifyAlbumInvite,
notifyAlbumUpdate,
userDelete,
userDeleteCheck,
userSyncUsage,
personCleanup,
personFileMigration,
personGenerateThumbnail,
sessionCleanup,
sendMail,
sidecarQueueAll,
sidecarCheck,
sidecarWrite,
smartSearchQueueAll,
smartSearch,
storageTemplateMigration,
storageTemplateMigrationSingle,
tagCleanup,
versionCheck,
ocrQueueAll,
ocr,
workflowRun,
];
static JobName? fromJson(dynamic value) => JobNameTypeTransformer().decode(value);
static List<JobName> listFromJson(dynamic json, {bool growable = false,}) {
final result = <JobName>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = JobName.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
}
/// Transformation class that can [encode] an instance of [JobName] to String,
/// and [decode] dynamic data back to [JobName].
class JobNameTypeTransformer {
factory JobNameTypeTransformer() => _instance ??= const JobNameTypeTransformer._();
const JobNameTypeTransformer._();
String encode(JobName data) => data.value;
/// Decodes a [dynamic value][data] to a JobName.
///
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
///
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
/// and users are still using an old app with the old code.
JobName? decode(dynamic data, {bool allowNull = true}) {
if (data != null) {
switch (data) {
case r'AssetDelete': return JobName.assetDelete;
case r'AssetDeleteCheck': return JobName.assetDeleteCheck;
case r'AssetDetectFacesQueueAll': return JobName.assetDetectFacesQueueAll;
case r'AssetDetectFaces': return JobName.assetDetectFaces;
case r'AssetDetectDuplicatesQueueAll': return JobName.assetDetectDuplicatesQueueAll;
case r'AssetDetectDuplicates': return JobName.assetDetectDuplicates;
case r'AssetEncodeVideoQueueAll': return JobName.assetEncodeVideoQueueAll;
case r'AssetEncodeVideo': return JobName.assetEncodeVideo;
case r'AssetEmptyTrash': return JobName.assetEmptyTrash;
case r'AssetExtractMetadataQueueAll': return JobName.assetExtractMetadataQueueAll;
case r'AssetExtractMetadata': return JobName.assetExtractMetadata;
case r'AssetFileMigration': return JobName.assetFileMigration;
case r'AssetGenerateThumbnailsQueueAll': return JobName.assetGenerateThumbnailsQueueAll;
case r'AssetGenerateThumbnails': return JobName.assetGenerateThumbnails;
case r'AuditLogCleanup': return JobName.auditLogCleanup;
case r'AuditTableCleanup': return JobName.auditTableCleanup;
case r'DatabaseBackup': return JobName.databaseBackup;
case r'FacialRecognitionQueueAll': return JobName.facialRecognitionQueueAll;
case r'FacialRecognition': return JobName.facialRecognition;
case r'FileDelete': return JobName.fileDelete;
case r'FileMigrationQueueAll': return JobName.fileMigrationQueueAll;
case r'LibraryDeleteCheck': return JobName.libraryDeleteCheck;
case r'LibraryDelete': return JobName.libraryDelete;
case r'LibraryRemoveAsset': return JobName.libraryRemoveAsset;
case r'LibraryScanAssetsQueueAll': return JobName.libraryScanAssetsQueueAll;
case r'LibrarySyncAssets': return JobName.librarySyncAssets;
case r'LibrarySyncFilesQueueAll': return JobName.librarySyncFilesQueueAll;
case r'LibrarySyncFiles': return JobName.librarySyncFiles;
case r'LibraryScanQueueAll': return JobName.libraryScanQueueAll;
case r'MemoryCleanup': return JobName.memoryCleanup;
case r'MemoryGenerate': return JobName.memoryGenerate;
case r'NotificationsCleanup': return JobName.notificationsCleanup;
case r'NotifyUserSignup': return JobName.notifyUserSignup;
case r'NotifyAlbumInvite': return JobName.notifyAlbumInvite;
case r'NotifyAlbumUpdate': return JobName.notifyAlbumUpdate;
case r'UserDelete': return JobName.userDelete;
case r'UserDeleteCheck': return JobName.userDeleteCheck;
case r'UserSyncUsage': return JobName.userSyncUsage;
case r'PersonCleanup': return JobName.personCleanup;
case r'PersonFileMigration': return JobName.personFileMigration;
case r'PersonGenerateThumbnail': return JobName.personGenerateThumbnail;
case r'SessionCleanup': return JobName.sessionCleanup;
case r'SendMail': return JobName.sendMail;
case r'SidecarQueueAll': return JobName.sidecarQueueAll;
case r'SidecarCheck': return JobName.sidecarCheck;
case r'SidecarWrite': return JobName.sidecarWrite;
case r'SmartSearchQueueAll': return JobName.smartSearchQueueAll;
case r'SmartSearch': return JobName.smartSearch;
case r'StorageTemplateMigration': return JobName.storageTemplateMigration;
case r'StorageTemplateMigrationSingle': return JobName.storageTemplateMigrationSingle;
case r'TagCleanup': return JobName.tagCleanup;
case r'VersionCheck': return JobName.versionCheck;
case r'OcrQueueAll': return JobName.ocrQueueAll;
case r'Ocr': return JobName.ocr;
case r'WorkflowRun': return JobName.workflowRun;
default:
if (!allowNull) {
throw ArgumentError('Unknown enum value to decode: $data');
}
}
}
return null;
}
/// Singleton [JobNameTypeTransformer] instance.
static JobNameTypeTransformer? _instance;
}

View File

@ -152,6 +152,12 @@ class Permission {
static const userProfileImagePeriodRead = Permission._(r'userProfileImage.read'); static const userProfileImagePeriodRead = Permission._(r'userProfileImage.read');
static const userProfileImagePeriodUpdate = Permission._(r'userProfileImage.update'); static const userProfileImagePeriodUpdate = Permission._(r'userProfileImage.update');
static const userProfileImagePeriodDelete = Permission._(r'userProfileImage.delete'); static const userProfileImagePeriodDelete = Permission._(r'userProfileImage.delete');
static const queuePeriodRead = Permission._(r'queue.read');
static const queuePeriodUpdate = Permission._(r'queue.update');
static const queueJobPeriodCreate = Permission._(r'queueJob.create');
static const queueJobPeriodRead = Permission._(r'queueJob.read');
static const queueJobPeriodUpdate = Permission._(r'queueJob.update');
static const queueJobPeriodDelete = Permission._(r'queueJob.delete');
static const workflowPeriodCreate = Permission._(r'workflow.create'); static const workflowPeriodCreate = Permission._(r'workflow.create');
static const workflowPeriodRead = Permission._(r'workflow.read'); static const workflowPeriodRead = Permission._(r'workflow.read');
static const workflowPeriodUpdate = Permission._(r'workflow.update'); static const workflowPeriodUpdate = Permission._(r'workflow.update');
@ -294,6 +300,12 @@ class Permission {
userProfileImagePeriodRead, userProfileImagePeriodRead,
userProfileImagePeriodUpdate, userProfileImagePeriodUpdate,
userProfileImagePeriodDelete, userProfileImagePeriodDelete,
queuePeriodRead,
queuePeriodUpdate,
queueJobPeriodCreate,
queueJobPeriodRead,
queueJobPeriodUpdate,
queueJobPeriodDelete,
workflowPeriodCreate, workflowPeriodCreate,
workflowPeriodRead, workflowPeriodRead,
workflowPeriodUpdate, workflowPeriodUpdate,
@ -471,6 +483,12 @@ class PermissionTypeTransformer {
case r'userProfileImage.read': return Permission.userProfileImagePeriodRead; case r'userProfileImage.read': return Permission.userProfileImagePeriodRead;
case r'userProfileImage.update': return Permission.userProfileImagePeriodUpdate; case r'userProfileImage.update': return Permission.userProfileImagePeriodUpdate;
case r'userProfileImage.delete': return Permission.userProfileImagePeriodDelete; case r'userProfileImage.delete': return Permission.userProfileImagePeriodDelete;
case r'queue.read': return Permission.queuePeriodRead;
case r'queue.update': return Permission.queuePeriodUpdate;
case r'queueJob.create': return Permission.queueJobPeriodCreate;
case r'queueJob.read': return Permission.queueJobPeriodRead;
case r'queueJob.update': return Permission.queueJobPeriodUpdate;
case r'queueJob.delete': return Permission.queueJobPeriodDelete;
case r'workflow.create': return Permission.workflowPeriodCreate; case r'workflow.create': return Permission.workflowPeriodCreate;
case r'workflow.read': return Permission.workflowPeriodRead; case r'workflow.read': return Permission.workflowPeriodRead;
case r'workflow.update': return Permission.workflowPeriodUpdate; case r'workflow.update': return Permission.workflowPeriodUpdate;

View File

@ -0,0 +1,109 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// 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 QueueDeleteDto {
/// Returns a new [QueueDeleteDto] instance.
QueueDeleteDto({
this.failed,
});
/// If true, will also remove failed jobs from the queue.
///
/// 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? failed;
@override
bool operator ==(Object other) => identical(this, other) || other is QueueDeleteDto &&
other.failed == failed;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(failed == null ? 0 : failed!.hashCode);
@override
String toString() => 'QueueDeleteDto[failed=$failed]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
if (this.failed != null) {
json[r'failed'] = this.failed;
} else {
// json[r'failed'] = null;
}
return json;
}
/// Returns a new [QueueDeleteDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static QueueDeleteDto? fromJson(dynamic value) {
upgradeDto(value, "QueueDeleteDto");
if (value is Map) {
final json = value.cast<String, dynamic>();
return QueueDeleteDto(
failed: mapValueOfType<bool>(json, r'failed'),
);
}
return null;
}
static List<QueueDeleteDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <QueueDeleteDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = QueueDeleteDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, QueueDeleteDto> mapFromJson(dynamic json) {
final map = <String, QueueDeleteDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = QueueDeleteDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of QueueDeleteDto-objects as value to a dart map
static Map<String, List<QueueDeleteDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<QueueDeleteDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = QueueDeleteDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
};
}

View File

@ -0,0 +1,132 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// 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 QueueJobResponseDto {
/// Returns a new [QueueJobResponseDto] instance.
QueueJobResponseDto({
required this.data,
this.id,
required this.name,
required this.timestamp,
});
Object data;
///
/// 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;
JobName name;
int timestamp;
@override
bool operator ==(Object other) => identical(this, other) || other is QueueJobResponseDto &&
other.data == data &&
other.id == id &&
other.name == name &&
other.timestamp == timestamp;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(data.hashCode) +
(id == null ? 0 : id!.hashCode) +
(name.hashCode) +
(timestamp.hashCode);
@override
String toString() => 'QueueJobResponseDto[data=$data, id=$id, name=$name, timestamp=$timestamp]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'data'] = this.data;
if (this.id != null) {
json[r'id'] = this.id;
} else {
// json[r'id'] = null;
}
json[r'name'] = this.name;
json[r'timestamp'] = this.timestamp;
return json;
}
/// Returns a new [QueueJobResponseDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static QueueJobResponseDto? fromJson(dynamic value) {
upgradeDto(value, "QueueJobResponseDto");
if (value is Map) {
final json = value.cast<String, dynamic>();
return QueueJobResponseDto(
data: mapValueOfType<Object>(json, r'data')!,
id: mapValueOfType<String>(json, r'id'),
name: JobName.fromJson(json[r'name'])!,
timestamp: mapValueOfType<int>(json, r'timestamp')!,
);
}
return null;
}
static List<QueueJobResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <QueueJobResponseDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = QueueJobResponseDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, QueueJobResponseDto> mapFromJson(dynamic json) {
final map = <String, QueueJobResponseDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = QueueJobResponseDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of QueueJobResponseDto-objects as value to a dart map
static Map<String, List<QueueJobResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<QueueJobResponseDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = QueueJobResponseDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'data',
'name',
'timestamp',
};
}

View File

@ -0,0 +1,97 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// 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 QueueJobStatus {
/// Instantiate a new enum with the provided [value].
const QueueJobStatus._(this.value);
/// The underlying value of this enum member.
final String value;
@override
String toString() => value;
String toJson() => value;
static const active = QueueJobStatus._(r'active');
static const failed = QueueJobStatus._(r'failed');
static const completed = QueueJobStatus._(r'completed');
static const delayed = QueueJobStatus._(r'delayed');
static const waiting = QueueJobStatus._(r'waiting');
static const paused = QueueJobStatus._(r'paused');
/// List of all possible values in this [enum][QueueJobStatus].
static const values = <QueueJobStatus>[
active,
failed,
completed,
delayed,
waiting,
paused,
];
static QueueJobStatus? fromJson(dynamic value) => QueueJobStatusTypeTransformer().decode(value);
static List<QueueJobStatus> listFromJson(dynamic json, {bool growable = false,}) {
final result = <QueueJobStatus>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = QueueJobStatus.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
}
/// Transformation class that can [encode] an instance of [QueueJobStatus] to String,
/// and [decode] dynamic data back to [QueueJobStatus].
class QueueJobStatusTypeTransformer {
factory QueueJobStatusTypeTransformer() => _instance ??= const QueueJobStatusTypeTransformer._();
const QueueJobStatusTypeTransformer._();
String encode(QueueJobStatus data) => data.value;
/// Decodes a [dynamic value][data] to a QueueJobStatus.
///
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
///
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
/// and users are still using an old app with the old code.
QueueJobStatus? decode(dynamic data, {bool allowNull = true}) {
if (data != null) {
switch (data) {
case r'active': return QueueJobStatus.active;
case r'failed': return QueueJobStatus.failed;
case r'completed': return QueueJobStatus.completed;
case r'delayed': return QueueJobStatus.delayed;
case r'waiting': return QueueJobStatus.waiting;
case r'paused': return QueueJobStatus.paused;
default:
if (!allowNull) {
throw ArgumentError('Unknown enum value to decode: $data');
}
}
}
return null;
}
/// Singleton [QueueJobStatusTypeTransformer] instance.
static QueueJobStatusTypeTransformer? _instance;
}

View File

@ -13,32 +13,38 @@ part of openapi.api;
class QueueResponseDto { class QueueResponseDto {
/// Returns a new [QueueResponseDto] instance. /// Returns a new [QueueResponseDto] instance.
QueueResponseDto({ QueueResponseDto({
required this.jobCounts, required this.isPaused,
required this.queueStatus, required this.name,
required this.statistics,
}); });
QueueStatisticsDto jobCounts; bool isPaused;
QueueStatusDto queueStatus; QueueName name;
QueueStatisticsDto statistics;
@override @override
bool operator ==(Object other) => identical(this, other) || other is QueueResponseDto && bool operator ==(Object other) => identical(this, other) || other is QueueResponseDto &&
other.jobCounts == jobCounts && other.isPaused == isPaused &&
other.queueStatus == queueStatus; other.name == name &&
other.statistics == statistics;
@override @override
int get hashCode => int get hashCode =>
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(jobCounts.hashCode) + (isPaused.hashCode) +
(queueStatus.hashCode); (name.hashCode) +
(statistics.hashCode);
@override @override
String toString() => 'QueueResponseDto[jobCounts=$jobCounts, queueStatus=$queueStatus]'; String toString() => 'QueueResponseDto[isPaused=$isPaused, name=$name, statistics=$statistics]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
json[r'jobCounts'] = this.jobCounts; json[r'isPaused'] = this.isPaused;
json[r'queueStatus'] = this.queueStatus; json[r'name'] = this.name;
json[r'statistics'] = this.statistics;
return json; return json;
} }
@ -51,8 +57,9 @@ class QueueResponseDto {
final json = value.cast<String, dynamic>(); final json = value.cast<String, dynamic>();
return QueueResponseDto( return QueueResponseDto(
jobCounts: QueueStatisticsDto.fromJson(json[r'jobCounts'])!, isPaused: mapValueOfType<bool>(json, r'isPaused')!,
queueStatus: QueueStatusDto.fromJson(json[r'queueStatus'])!, name: QueueName.fromJson(json[r'name'])!,
statistics: QueueStatisticsDto.fromJson(json[r'statistics'])!,
); );
} }
return null; return null;
@ -100,8 +107,9 @@ class QueueResponseDto {
/// The list of required keys that must be present in a JSON. /// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{ static const requiredKeys = <String>{
'jobCounts', 'isPaused',
'queueStatus', 'name',
'statistics',
}; };
} }

View File

@ -0,0 +1,107 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// 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 QueueResponseLegacyDto {
/// Returns a new [QueueResponseLegacyDto] instance.
QueueResponseLegacyDto({
required this.jobCounts,
required this.queueStatus,
});
QueueStatisticsDto jobCounts;
QueueStatusLegacyDto queueStatus;
@override
bool operator ==(Object other) => identical(this, other) || other is QueueResponseLegacyDto &&
other.jobCounts == jobCounts &&
other.queueStatus == queueStatus;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(jobCounts.hashCode) +
(queueStatus.hashCode);
@override
String toString() => 'QueueResponseLegacyDto[jobCounts=$jobCounts, queueStatus=$queueStatus]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'jobCounts'] = this.jobCounts;
json[r'queueStatus'] = this.queueStatus;
return json;
}
/// Returns a new [QueueResponseLegacyDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static QueueResponseLegacyDto? fromJson(dynamic value) {
upgradeDto(value, "QueueResponseLegacyDto");
if (value is Map) {
final json = value.cast<String, dynamic>();
return QueueResponseLegacyDto(
jobCounts: QueueStatisticsDto.fromJson(json[r'jobCounts'])!,
queueStatus: QueueStatusLegacyDto.fromJson(json[r'queueStatus'])!,
);
}
return null;
}
static List<QueueResponseLegacyDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <QueueResponseLegacyDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = QueueResponseLegacyDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, QueueResponseLegacyDto> mapFromJson(dynamic json) {
final map = <String, QueueResponseLegacyDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = QueueResponseLegacyDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of QueueResponseLegacyDto-objects as value to a dart map
static Map<String, List<QueueResponseLegacyDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<QueueResponseLegacyDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = QueueResponseLegacyDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'jobCounts',
'queueStatus',
};
}

View File

@ -10,9 +10,9 @@
part of openapi.api; part of openapi.api;
class QueueStatusDto { class QueueStatusLegacyDto {
/// Returns a new [QueueStatusDto] instance. /// Returns a new [QueueStatusLegacyDto] instance.
QueueStatusDto({ QueueStatusLegacyDto({
required this.isActive, required this.isActive,
required this.isPaused, required this.isPaused,
}); });
@ -22,7 +22,7 @@ class QueueStatusDto {
bool isPaused; bool isPaused;
@override @override
bool operator ==(Object other) => identical(this, other) || other is QueueStatusDto && bool operator ==(Object other) => identical(this, other) || other is QueueStatusLegacyDto &&
other.isActive == isActive && other.isActive == isActive &&
other.isPaused == isPaused; other.isPaused == isPaused;
@ -33,7 +33,7 @@ class QueueStatusDto {
(isPaused.hashCode); (isPaused.hashCode);
@override @override
String toString() => 'QueueStatusDto[isActive=$isActive, isPaused=$isPaused]'; String toString() => 'QueueStatusLegacyDto[isActive=$isActive, isPaused=$isPaused]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@ -42,15 +42,15 @@ class QueueStatusDto {
return json; return json;
} }
/// Returns a new [QueueStatusDto] instance and imports its values from /// Returns a new [QueueStatusLegacyDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise. /// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods // ignore: prefer_constructors_over_static_methods
static QueueStatusDto? fromJson(dynamic value) { static QueueStatusLegacyDto? fromJson(dynamic value) {
upgradeDto(value, "QueueStatusDto"); upgradeDto(value, "QueueStatusLegacyDto");
if (value is Map) { if (value is Map) {
final json = value.cast<String, dynamic>(); final json = value.cast<String, dynamic>();
return QueueStatusDto( return QueueStatusLegacyDto(
isActive: mapValueOfType<bool>(json, r'isActive')!, isActive: mapValueOfType<bool>(json, r'isActive')!,
isPaused: mapValueOfType<bool>(json, r'isPaused')!, isPaused: mapValueOfType<bool>(json, r'isPaused')!,
); );
@ -58,11 +58,11 @@ class QueueStatusDto {
return null; return null;
} }
static List<QueueStatusDto> listFromJson(dynamic json, {bool growable = false,}) { static List<QueueStatusLegacyDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <QueueStatusDto>[]; final result = <QueueStatusLegacyDto>[];
if (json is List && json.isNotEmpty) { if (json is List && json.isNotEmpty) {
for (final row in json) { for (final row in json) {
final value = QueueStatusDto.fromJson(row); final value = QueueStatusLegacyDto.fromJson(row);
if (value != null) { if (value != null) {
result.add(value); result.add(value);
} }
@ -71,12 +71,12 @@ class QueueStatusDto {
return result.toList(growable: growable); return result.toList(growable: growable);
} }
static Map<String, QueueStatusDto> mapFromJson(dynamic json) { static Map<String, QueueStatusLegacyDto> mapFromJson(dynamic json) {
final map = <String, QueueStatusDto>{}; final map = <String, QueueStatusLegacyDto>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) { for (final entry in json.entries) {
final value = QueueStatusDto.fromJson(entry.value); final value = QueueStatusLegacyDto.fromJson(entry.value);
if (value != null) { if (value != null) {
map[entry.key] = value; map[entry.key] = value;
} }
@ -85,14 +85,14 @@ class QueueStatusDto {
return map; return map;
} }
// maps a json object with a list of QueueStatusDto-objects as value to a dart map // maps a json object with a list of QueueStatusLegacyDto-objects as value to a dart map
static Map<String, List<QueueStatusDto>> mapListFromJson(dynamic json, {bool growable = false,}) { static Map<String, List<QueueStatusLegacyDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<QueueStatusDto>>{}; final map = <String, List<QueueStatusLegacyDto>>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments // ignore: parameter_assignments
json = json.cast<String, dynamic>(); json = json.cast<String, dynamic>();
for (final entry in json.entries) { for (final entry in json.entries) {
map[entry.key] = QueueStatusDto.listFromJson(entry.value, growable: growable,); map[entry.key] = QueueStatusLegacyDto.listFromJson(entry.value, growable: growable,);
} }
} }
return map; return map;

View File

@ -0,0 +1,108 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// 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 QueueUpdateDto {
/// Returns a new [QueueUpdateDto] instance.
QueueUpdateDto({
this.isPaused,
});
///
/// 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? isPaused;
@override
bool operator ==(Object other) => identical(this, other) || other is QueueUpdateDto &&
other.isPaused == isPaused;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(isPaused == null ? 0 : isPaused!.hashCode);
@override
String toString() => 'QueueUpdateDto[isPaused=$isPaused]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
if (this.isPaused != null) {
json[r'isPaused'] = this.isPaused;
} else {
// json[r'isPaused'] = null;
}
return json;
}
/// Returns a new [QueueUpdateDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static QueueUpdateDto? fromJson(dynamic value) {
upgradeDto(value, "QueueUpdateDto");
if (value is Map) {
final json = value.cast<String, dynamic>();
return QueueUpdateDto(
isPaused: mapValueOfType<bool>(json, r'isPaused'),
);
}
return null;
}
static List<QueueUpdateDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <QueueUpdateDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = QueueUpdateDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, QueueUpdateDto> mapFromJson(dynamic json) {
final map = <String, QueueUpdateDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = QueueUpdateDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of QueueUpdateDto-objects as value to a dart map
static Map<String, List<QueueUpdateDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<QueueUpdateDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = QueueUpdateDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
};
}

View File

@ -10,9 +10,9 @@
part of openapi.api; part of openapi.api;
class QueuesResponseDto { class QueuesResponseLegacyDto {
/// Returns a new [QueuesResponseDto] instance. /// Returns a new [QueuesResponseLegacyDto] instance.
QueuesResponseDto({ QueuesResponseLegacyDto({
required this.backgroundTask, required this.backgroundTask,
required this.backupDatabase, required this.backupDatabase,
required this.duplicateDetection, required this.duplicateDetection,
@ -32,42 +32,42 @@ class QueuesResponseDto {
required this.workflow, required this.workflow,
}); });
QueueResponseDto backgroundTask; QueueResponseLegacyDto backgroundTask;
QueueResponseDto backupDatabase; QueueResponseLegacyDto backupDatabase;
QueueResponseDto duplicateDetection; QueueResponseLegacyDto duplicateDetection;
QueueResponseDto faceDetection; QueueResponseLegacyDto faceDetection;
QueueResponseDto facialRecognition; QueueResponseLegacyDto facialRecognition;
QueueResponseDto library_; QueueResponseLegacyDto library_;
QueueResponseDto metadataExtraction; QueueResponseLegacyDto metadataExtraction;
QueueResponseDto migration; QueueResponseLegacyDto migration;
QueueResponseDto notifications; QueueResponseLegacyDto notifications;
QueueResponseDto ocr; QueueResponseLegacyDto ocr;
QueueResponseDto search; QueueResponseLegacyDto search;
QueueResponseDto sidecar; QueueResponseLegacyDto sidecar;
QueueResponseDto smartSearch; QueueResponseLegacyDto smartSearch;
QueueResponseDto storageTemplateMigration; QueueResponseLegacyDto storageTemplateMigration;
QueueResponseDto thumbnailGeneration; QueueResponseLegacyDto thumbnailGeneration;
QueueResponseDto videoConversion; QueueResponseLegacyDto videoConversion;
QueueResponseDto workflow; QueueResponseLegacyDto workflow;
@override @override
bool operator ==(Object other) => identical(this, other) || other is QueuesResponseDto && bool operator ==(Object other) => identical(this, other) || other is QueuesResponseLegacyDto &&
other.backgroundTask == backgroundTask && other.backgroundTask == backgroundTask &&
other.backupDatabase == backupDatabase && other.backupDatabase == backupDatabase &&
other.duplicateDetection == duplicateDetection && other.duplicateDetection == duplicateDetection &&
@ -108,7 +108,7 @@ class QueuesResponseDto {
(workflow.hashCode); (workflow.hashCode);
@override @override
String toString() => 'QueuesResponseDto[backgroundTask=$backgroundTask, backupDatabase=$backupDatabase, duplicateDetection=$duplicateDetection, faceDetection=$faceDetection, facialRecognition=$facialRecognition, library_=$library_, metadataExtraction=$metadataExtraction, migration=$migration, notifications=$notifications, ocr=$ocr, search=$search, sidecar=$sidecar, smartSearch=$smartSearch, storageTemplateMigration=$storageTemplateMigration, thumbnailGeneration=$thumbnailGeneration, videoConversion=$videoConversion, workflow=$workflow]'; String toString() => 'QueuesResponseLegacyDto[backgroundTask=$backgroundTask, backupDatabase=$backupDatabase, duplicateDetection=$duplicateDetection, faceDetection=$faceDetection, facialRecognition=$facialRecognition, library_=$library_, metadataExtraction=$metadataExtraction, migration=$migration, notifications=$notifications, ocr=$ocr, search=$search, sidecar=$sidecar, smartSearch=$smartSearch, storageTemplateMigration=$storageTemplateMigration, thumbnailGeneration=$thumbnailGeneration, videoConversion=$videoConversion, workflow=$workflow]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@ -132,42 +132,42 @@ class QueuesResponseDto {
return json; return json;
} }
/// Returns a new [QueuesResponseDto] instance and imports its values from /// Returns a new [QueuesResponseLegacyDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise. /// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods // ignore: prefer_constructors_over_static_methods
static QueuesResponseDto? fromJson(dynamic value) { static QueuesResponseLegacyDto? fromJson(dynamic value) {
upgradeDto(value, "QueuesResponseDto"); upgradeDto(value, "QueuesResponseLegacyDto");
if (value is Map) { if (value is Map) {
final json = value.cast<String, dynamic>(); final json = value.cast<String, dynamic>();
return QueuesResponseDto( return QueuesResponseLegacyDto(
backgroundTask: QueueResponseDto.fromJson(json[r'backgroundTask'])!, backgroundTask: QueueResponseLegacyDto.fromJson(json[r'backgroundTask'])!,
backupDatabase: QueueResponseDto.fromJson(json[r'backupDatabase'])!, backupDatabase: QueueResponseLegacyDto.fromJson(json[r'backupDatabase'])!,
duplicateDetection: QueueResponseDto.fromJson(json[r'duplicateDetection'])!, duplicateDetection: QueueResponseLegacyDto.fromJson(json[r'duplicateDetection'])!,
faceDetection: QueueResponseDto.fromJson(json[r'faceDetection'])!, faceDetection: QueueResponseLegacyDto.fromJson(json[r'faceDetection'])!,
facialRecognition: QueueResponseDto.fromJson(json[r'facialRecognition'])!, facialRecognition: QueueResponseLegacyDto.fromJson(json[r'facialRecognition'])!,
library_: QueueResponseDto.fromJson(json[r'library'])!, library_: QueueResponseLegacyDto.fromJson(json[r'library'])!,
metadataExtraction: QueueResponseDto.fromJson(json[r'metadataExtraction'])!, metadataExtraction: QueueResponseLegacyDto.fromJson(json[r'metadataExtraction'])!,
migration: QueueResponseDto.fromJson(json[r'migration'])!, migration: QueueResponseLegacyDto.fromJson(json[r'migration'])!,
notifications: QueueResponseDto.fromJson(json[r'notifications'])!, notifications: QueueResponseLegacyDto.fromJson(json[r'notifications'])!,
ocr: QueueResponseDto.fromJson(json[r'ocr'])!, ocr: QueueResponseLegacyDto.fromJson(json[r'ocr'])!,
search: QueueResponseDto.fromJson(json[r'search'])!, search: QueueResponseLegacyDto.fromJson(json[r'search'])!,
sidecar: QueueResponseDto.fromJson(json[r'sidecar'])!, sidecar: QueueResponseLegacyDto.fromJson(json[r'sidecar'])!,
smartSearch: QueueResponseDto.fromJson(json[r'smartSearch'])!, smartSearch: QueueResponseLegacyDto.fromJson(json[r'smartSearch'])!,
storageTemplateMigration: QueueResponseDto.fromJson(json[r'storageTemplateMigration'])!, storageTemplateMigration: QueueResponseLegacyDto.fromJson(json[r'storageTemplateMigration'])!,
thumbnailGeneration: QueueResponseDto.fromJson(json[r'thumbnailGeneration'])!, thumbnailGeneration: QueueResponseLegacyDto.fromJson(json[r'thumbnailGeneration'])!,
videoConversion: QueueResponseDto.fromJson(json[r'videoConversion'])!, videoConversion: QueueResponseLegacyDto.fromJson(json[r'videoConversion'])!,
workflow: QueueResponseDto.fromJson(json[r'workflow'])!, workflow: QueueResponseLegacyDto.fromJson(json[r'workflow'])!,
); );
} }
return null; return null;
} }
static List<QueuesResponseDto> listFromJson(dynamic json, {bool growable = false,}) { static List<QueuesResponseLegacyDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <QueuesResponseDto>[]; final result = <QueuesResponseLegacyDto>[];
if (json is List && json.isNotEmpty) { if (json is List && json.isNotEmpty) {
for (final row in json) { for (final row in json) {
final value = QueuesResponseDto.fromJson(row); final value = QueuesResponseLegacyDto.fromJson(row);
if (value != null) { if (value != null) {
result.add(value); result.add(value);
} }
@ -176,12 +176,12 @@ class QueuesResponseDto {
return result.toList(growable: growable); return result.toList(growable: growable);
} }
static Map<String, QueuesResponseDto> mapFromJson(dynamic json) { static Map<String, QueuesResponseLegacyDto> mapFromJson(dynamic json) {
final map = <String, QueuesResponseDto>{}; final map = <String, QueuesResponseLegacyDto>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) { for (final entry in json.entries) {
final value = QueuesResponseDto.fromJson(entry.value); final value = QueuesResponseLegacyDto.fromJson(entry.value);
if (value != null) { if (value != null) {
map[entry.key] = value; map[entry.key] = value;
} }
@ -190,14 +190,14 @@ class QueuesResponseDto {
return map; return map;
} }
// maps a json object with a list of QueuesResponseDto-objects as value to a dart map // maps a json object with a list of QueuesResponseLegacyDto-objects as value to a dart map
static Map<String, List<QueuesResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) { static Map<String, List<QueuesResponseLegacyDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<QueuesResponseDto>>{}; final map = <String, List<QueuesResponseLegacyDto>>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments // ignore: parameter_assignments
json = json.cast<String, dynamic>(); json = json.cast<String, dynamic>();
for (final entry in json.entries) { for (final entry in json.entries) {
map[entry.key] = QueuesResponseDto.listFromJson(entry.value, growable: growable,); map[entry.key] = QueuesResponseLegacyDto.listFromJson(entry.value, growable: growable,);
} }
} }
return map; return map;

View File

@ -4929,6 +4929,7 @@
}, },
"/jobs": { "/jobs": {
"get": { "get": {
"deprecated": true,
"description": "Retrieve the counts of the current queue, as well as the current status.", "description": "Retrieve the counts of the current queue, as well as the current status.",
"operationId": "getQueuesLegacy", "operationId": "getQueuesLegacy",
"parameters": [], "parameters": [],
@ -4937,7 +4938,7 @@
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"$ref": "#/components/schemas/QueuesResponseDto" "$ref": "#/components/schemas/QueuesResponseLegacyDto"
} }
} }
}, },
@ -4957,7 +4958,8 @@
], ],
"summary": "Retrieve queue counts and status", "summary": "Retrieve queue counts and status",
"tags": [ "tags": [
"Jobs" "Jobs",
"Deprecated"
], ],
"x-immich-admin-only": true, "x-immich-admin-only": true,
"x-immich-history": [ "x-immich-history": [
@ -4972,10 +4974,14 @@
{ {
"version": "v2", "version": "v2",
"state": "Stable" "state": "Stable"
},
{
"version": "v2.4.0",
"state": "Deprecated"
} }
], ],
"x-immich-permission": "job.read", "x-immich-permission": "job.read",
"x-immich-state": "Stable" "x-immich-state": "Deprecated"
}, },
"post": { "post": {
"description": "Run a specific job. Most jobs are queued automatically, but this endpoint allows for manual creation of a handful of jobs, including various cleanup tasks, as well as creating a new database backup.", "description": "Run a specific job. Most jobs are queued automatically, but this endpoint allows for manual creation of a handful of jobs, including various cleanup tasks, as well as creating a new database backup.",
@ -5032,6 +5038,7 @@
}, },
"/jobs/{name}": { "/jobs/{name}": {
"put": { "put": {
"deprecated": true,
"description": "Queue all assets for a specific job type. Defaults to only queueing assets that have not yet been processed, but the force command can be used to re-process all assets.", "description": "Queue all assets for a specific job type. Defaults to only queueing assets that have not yet been processed, but the force command can be used to re-process all assets.",
"operationId": "runQueueCommandLegacy", "operationId": "runQueueCommandLegacy",
"parameters": [ "parameters": [
@ -5059,7 +5066,7 @@
"content": { "content": {
"application/json": { "application/json": {
"schema": { "schema": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
} }
} }
}, },
@ -5079,7 +5086,8 @@
], ],
"summary": "Run jobs", "summary": "Run jobs",
"tags": [ "tags": [
"Jobs" "Jobs",
"Deprecated"
], ],
"x-immich-admin-only": true, "x-immich-admin-only": true,
"x-immich-history": [ "x-immich-history": [
@ -5094,10 +5102,14 @@
{ {
"version": "v2", "version": "v2",
"state": "Stable" "state": "Stable"
},
{
"version": "v2.4.0",
"state": "Deprecated"
} }
], ],
"x-immich-permission": "job.create", "x-immich-permission": "job.create",
"x-immich-state": "Stable" "x-immich-state": "Deprecated"
} }
}, },
"/libraries": { "/libraries": {
@ -8064,6 +8076,303 @@
"x-immich-state": "Alpha" "x-immich-state": "Alpha"
} }
}, },
"/queues": {
"get": {
"description": "Retrieves a list of queues.",
"operationId": "getQueues",
"parameters": [],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/QueueResponseDto"
},
"type": "array"
}
}
},
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"summary": "List all queues",
"tags": [
"Queues"
],
"x-immich-admin-only": true,
"x-immich-history": [
{
"version": "v2.4.0",
"state": "Added"
},
{
"version": "v2.4.0",
"state": "Alpha"
}
],
"x-immich-permission": "queue.read",
"x-immich-state": "Alpha"
}
},
"/queues/{name}": {
"get": {
"description": "Retrieves a specific queue by its name.",
"operationId": "getQueue",
"parameters": [
{
"name": "name",
"required": true,
"in": "path",
"schema": {
"$ref": "#/components/schemas/QueueName"
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/QueueResponseDto"
}
}
},
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"summary": "Retrieve a queue",
"tags": [
"Queues"
],
"x-immich-admin-only": true,
"x-immich-history": [
{
"version": "v2.4.0",
"state": "Added"
},
{
"version": "v2.4.0",
"state": "Alpha"
}
],
"x-immich-permission": "queue.read",
"x-immich-state": "Alpha"
},
"put": {
"description": "Change the paused status of a specific queue.",
"operationId": "updateQueue",
"parameters": [
{
"name": "name",
"required": true,
"in": "path",
"schema": {
"$ref": "#/components/schemas/QueueName"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/QueueUpdateDto"
}
}
},
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/QueueResponseDto"
}
}
},
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"summary": "Update a queue",
"tags": [
"Queues"
],
"x-immich-admin-only": true,
"x-immich-history": [
{
"version": "v2.4.0",
"state": "Added"
},
{
"version": "v2.4.0",
"state": "Alpha"
}
],
"x-immich-permission": "queue.update",
"x-immich-state": "Alpha"
}
},
"/queues/{name}/jobs": {
"delete": {
"description": "Removes all jobs from the specified queue.",
"operationId": "emptyQueue",
"parameters": [
{
"name": "name",
"required": true,
"in": "path",
"schema": {
"$ref": "#/components/schemas/QueueName"
}
}
],
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/QueueDeleteDto"
}
}
},
"required": true
},
"responses": {
"204": {
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"summary": "Empty a queue",
"tags": [
"Queues"
],
"x-immich-admin-only": true,
"x-immich-history": [
{
"version": "v2.4.0",
"state": "Added"
},
{
"version": "v2.4.0",
"state": "Alpha"
}
],
"x-immich-permission": "queueJob.delete",
"x-immich-state": "Alpha"
},
"get": {
"description": "Retrieves a list of queue jobs from the specified queue.",
"operationId": "getQueueJobs",
"parameters": [
{
"name": "name",
"required": true,
"in": "path",
"schema": {
"$ref": "#/components/schemas/QueueName"
}
},
{
"name": "status",
"required": false,
"in": "query",
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/QueueJobStatus"
}
}
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"items": {
"$ref": "#/components/schemas/QueueJobResponseDto"
},
"type": "array"
}
}
},
"description": ""
}
},
"security": [
{
"bearer": []
},
{
"cookie": []
},
{
"api_key": []
}
],
"summary": "Retrieve queue jobs",
"tags": [
"Queues"
],
"x-immich-admin-only": true,
"x-immich-history": [
{
"version": "v2.4.0",
"state": "Added"
},
{
"version": "v2.4.0",
"state": "Alpha"
}
],
"x-immich-permission": "queueJob.read",
"x-immich-state": "Alpha"
}
},
"/search/cities": { "/search/cities": {
"get": { "get": {
"description": "Retrieve a list of assets with each asset belonging to a different city. This endpoint is used on the places pages to show a single thumbnail for each city the user has assets in.", "description": "Retrieve a list of assets with each asset belonging to a different city. This endpoint is used on the places pages to show a single thumbnail for each city the user has assets in.",
@ -14043,6 +14352,10 @@
"name": "Plugins", "name": "Plugins",
"description": "A plugin is an installed module that makes filters and actions available for the workflow feature." "description": "A plugin is an installed module that makes filters and actions available for the workflow feature."
}, },
{
"name": "Queues",
"description": "Queues and background jobs are used for processing tasks asynchronously. Queues can be paused and resumed as needed."
},
{ {
"name": "Search", "name": "Search",
"description": "Endpoints related to searching assets via text, smart search, optical character recognition (OCR), and other filters like person, album, and other metadata. Search endpoints usually support pagination and sorting." "description": "Endpoints related to searching assets via text, smart search, optical character recognition (OCR), and other filters like person, album, and other metadata. Search endpoints usually support pagination and sorting."
@ -16291,6 +16604,66 @@
], ],
"type": "object" "type": "object"
}, },
"JobName": {
"enum": [
"AssetDelete",
"AssetDeleteCheck",
"AssetDetectFacesQueueAll",
"AssetDetectFaces",
"AssetDetectDuplicatesQueueAll",
"AssetDetectDuplicates",
"AssetEncodeVideoQueueAll",
"AssetEncodeVideo",
"AssetEmptyTrash",
"AssetExtractMetadataQueueAll",
"AssetExtractMetadata",
"AssetFileMigration",
"AssetGenerateThumbnailsQueueAll",
"AssetGenerateThumbnails",
"AuditLogCleanup",
"AuditTableCleanup",
"DatabaseBackup",
"FacialRecognitionQueueAll",
"FacialRecognition",
"FileDelete",
"FileMigrationQueueAll",
"LibraryDeleteCheck",
"LibraryDelete",
"LibraryRemoveAsset",
"LibraryScanAssetsQueueAll",
"LibrarySyncAssets",
"LibrarySyncFilesQueueAll",
"LibrarySyncFiles",
"LibraryScanQueueAll",
"MemoryCleanup",
"MemoryGenerate",
"NotificationsCleanup",
"NotifyUserSignup",
"NotifyAlbumInvite",
"NotifyAlbumUpdate",
"UserDelete",
"UserDeleteCheck",
"UserSyncUsage",
"PersonCleanup",
"PersonFileMigration",
"PersonGenerateThumbnail",
"SessionCleanup",
"SendMail",
"SidecarQueueAll",
"SidecarCheck",
"SidecarWrite",
"SmartSearchQueueAll",
"SmartSearch",
"StorageTemplateMigration",
"StorageTemplateMigrationSingle",
"TagCleanup",
"VersionCheck",
"OcrQueueAll",
"Ocr",
"WorkflowRun"
],
"type": "string"
},
"JobSettingsDto": { "JobSettingsDto": {
"properties": { "properties": {
"concurrency": { "concurrency": {
@ -17583,6 +17956,12 @@
"userProfileImage.read", "userProfileImage.read",
"userProfileImage.update", "userProfileImage.update",
"userProfileImage.delete", "userProfileImage.delete",
"queue.read",
"queue.update",
"queueJob.create",
"queueJob.read",
"queueJob.update",
"queueJob.delete",
"workflow.create", "workflow.create",
"workflow.read", "workflow.read",
"workflow.update", "workflow.update",
@ -18083,6 +18462,63 @@
], ],
"type": "object" "type": "object"
}, },
"QueueDeleteDto": {
"properties": {
"failed": {
"description": "If true, will also remove failed jobs from the queue.",
"type": "boolean",
"x-immich-history": [
{
"version": "v2.4.0",
"state": "Added"
},
{
"version": "v2.4.0",
"state": "Alpha"
}
],
"x-immich-state": "Alpha"
}
},
"type": "object"
},
"QueueJobResponseDto": {
"properties": {
"data": {
"type": "object"
},
"id": {
"type": "string"
},
"name": {
"allOf": [
{
"$ref": "#/components/schemas/JobName"
}
]
},
"timestamp": {
"type": "integer"
}
},
"required": [
"data",
"name",
"timestamp"
],
"type": "object"
},
"QueueJobStatus": {
"enum": [
"active",
"failed",
"completed",
"delayed",
"waiting",
"paused"
],
"type": "string"
},
"QueueName": { "QueueName": {
"enum": [ "enum": [
"thumbnailGeneration", "thumbnailGeneration",
@ -18106,12 +18542,35 @@
"type": "string" "type": "string"
}, },
"QueueResponseDto": { "QueueResponseDto": {
"properties": {
"isPaused": {
"type": "boolean"
},
"name": {
"allOf": [
{
"$ref": "#/components/schemas/QueueName"
}
]
},
"statistics": {
"$ref": "#/components/schemas/QueueStatisticsDto"
}
},
"required": [
"isPaused",
"name",
"statistics"
],
"type": "object"
},
"QueueResponseLegacyDto": {
"properties": { "properties": {
"jobCounts": { "jobCounts": {
"$ref": "#/components/schemas/QueueStatisticsDto" "$ref": "#/components/schemas/QueueStatisticsDto"
}, },
"queueStatus": { "queueStatus": {
"$ref": "#/components/schemas/QueueStatusDto" "$ref": "#/components/schemas/QueueStatusLegacyDto"
} }
}, },
"required": [ "required": [
@ -18151,7 +18610,7 @@
], ],
"type": "object" "type": "object"
}, },
"QueueStatusDto": { "QueueStatusLegacyDto": {
"properties": { "properties": {
"isActive": { "isActive": {
"type": "boolean" "type": "boolean"
@ -18166,58 +18625,66 @@
], ],
"type": "object" "type": "object"
}, },
"QueuesResponseDto": { "QueueUpdateDto": {
"properties": {
"isPaused": {
"type": "boolean"
}
},
"type": "object"
},
"QueuesResponseLegacyDto": {
"properties": { "properties": {
"backgroundTask": { "backgroundTask": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"backupDatabase": { "backupDatabase": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"duplicateDetection": { "duplicateDetection": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"faceDetection": { "faceDetection": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"facialRecognition": { "facialRecognition": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"library": { "library": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"metadataExtraction": { "metadataExtraction": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"migration": { "migration": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"notifications": { "notifications": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"ocr": { "ocr": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"search": { "search": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"sidecar": { "sidecar": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"smartSearch": { "smartSearch": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"storageTemplateMigration": { "storageTemplateMigration": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"thumbnailGeneration": { "thumbnailGeneration": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"videoConversion": { "videoConversion": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
}, },
"workflow": { "workflow": {
"$ref": "#/components/schemas/QueueResponseDto" "$ref": "#/components/schemas/QueueResponseLegacyDto"
} }
}, },
"required": [ "required": [

View File

@ -716,32 +716,32 @@ export type QueueStatisticsDto = {
paused: number; paused: number;
waiting: number; waiting: number;
}; };
export type QueueStatusDto = { export type QueueStatusLegacyDto = {
isActive: boolean; isActive: boolean;
isPaused: boolean; isPaused: boolean;
}; };
export type QueueResponseDto = { export type QueueResponseLegacyDto = {
jobCounts: QueueStatisticsDto; jobCounts: QueueStatisticsDto;
queueStatus: QueueStatusDto; queueStatus: QueueStatusLegacyDto;
}; };
export type QueuesResponseDto = { export type QueuesResponseLegacyDto = {
backgroundTask: QueueResponseDto; backgroundTask: QueueResponseLegacyDto;
backupDatabase: QueueResponseDto; backupDatabase: QueueResponseLegacyDto;
duplicateDetection: QueueResponseDto; duplicateDetection: QueueResponseLegacyDto;
faceDetection: QueueResponseDto; faceDetection: QueueResponseLegacyDto;
facialRecognition: QueueResponseDto; facialRecognition: QueueResponseLegacyDto;
library: QueueResponseDto; library: QueueResponseLegacyDto;
metadataExtraction: QueueResponseDto; metadataExtraction: QueueResponseLegacyDto;
migration: QueueResponseDto; migration: QueueResponseLegacyDto;
notifications: QueueResponseDto; notifications: QueueResponseLegacyDto;
ocr: QueueResponseDto; ocr: QueueResponseLegacyDto;
search: QueueResponseDto; search: QueueResponseLegacyDto;
sidecar: QueueResponseDto; sidecar: QueueResponseLegacyDto;
smartSearch: QueueResponseDto; smartSearch: QueueResponseLegacyDto;
storageTemplateMigration: QueueResponseDto; storageTemplateMigration: QueueResponseLegacyDto;
thumbnailGeneration: QueueResponseDto; thumbnailGeneration: QueueResponseLegacyDto;
videoConversion: QueueResponseDto; videoConversion: QueueResponseLegacyDto;
workflow: QueueResponseDto; workflow: QueueResponseLegacyDto;
}; };
export type JobCreateDto = { export type JobCreateDto = {
name: ManualJobName; name: ManualJobName;
@ -966,6 +966,24 @@ export type PluginResponseDto = {
updatedAt: string; updatedAt: string;
version: string; version: string;
}; };
export type QueueResponseDto = {
isPaused: boolean;
name: QueueName;
statistics: QueueStatisticsDto;
};
export type QueueUpdateDto = {
isPaused?: boolean;
};
export type QueueDeleteDto = {
/** If true, will also remove failed jobs from the queue. */
failed?: boolean;
};
export type QueueJobResponseDto = {
data: object;
id?: string;
name: JobName;
timestamp: number;
};
export type SearchExploreItem = { export type SearchExploreItem = {
data: AssetResponseDto; data: AssetResponseDto;
value: string; value: string;
@ -2925,7 +2943,7 @@ export function reassignFacesById({ id, faceDto }: {
export function getQueuesLegacy(opts?: Oazapfts.RequestOpts) { export function getQueuesLegacy(opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{ return oazapfts.ok(oazapfts.fetchJson<{
status: 200; status: 200;
data: QueuesResponseDto; data: QueuesResponseLegacyDto;
}>("/jobs", { }>("/jobs", {
...opts ...opts
})); }));
@ -2951,7 +2969,7 @@ export function runQueueCommandLegacy({ name, queueCommandDto }: {
}, opts?: Oazapfts.RequestOpts) { }, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{ return oazapfts.ok(oazapfts.fetchJson<{
status: 200; status: 200;
data: QueueResponseDto; data: QueueResponseLegacyDto;
}>(`/jobs/${encodeURIComponent(name)}`, oazapfts.json({ }>(`/jobs/${encodeURIComponent(name)}`, oazapfts.json({
...opts, ...opts,
method: "PUT", method: "PUT",
@ -3651,6 +3669,75 @@ export function getPlugin({ id }: {
...opts ...opts
})); }));
} }
/**
* List all queues
*/
export function getQueues(opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: QueueResponseDto[];
}>("/queues", {
...opts
}));
}
/**
* Retrieve a queue
*/
export function getQueue({ name }: {
name: QueueName;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: QueueResponseDto;
}>(`/queues/${encodeURIComponent(name)}`, {
...opts
}));
}
/**
* Update a queue
*/
export function updateQueue({ name, queueUpdateDto }: {
name: QueueName;
queueUpdateDto: QueueUpdateDto;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: QueueResponseDto;
}>(`/queues/${encodeURIComponent(name)}`, oazapfts.json({
...opts,
method: "PUT",
body: queueUpdateDto
})));
}
/**
* Empty a queue
*/
export function emptyQueue({ name, queueDeleteDto }: {
name: QueueName;
queueDeleteDto: QueueDeleteDto;
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchText(`/queues/${encodeURIComponent(name)}/jobs`, oazapfts.json({
...opts,
method: "DELETE",
body: queueDeleteDto
})));
}
/**
* Retrieve queue jobs
*/
export function getQueueJobs({ name, status }: {
name: QueueName;
status?: QueueJobStatus[];
}, opts?: Oazapfts.RequestOpts) {
return oazapfts.ok(oazapfts.fetchJson<{
status: 200;
data: QueueJobResponseDto[];
}>(`/queues/${encodeURIComponent(name)}/jobs${QS.query(QS.explode({
status
}))}`, {
...opts
}));
}
/** /**
* Retrieve assets by city * Retrieve assets by city
*/ */
@ -5241,6 +5328,12 @@ export enum Permission {
UserProfileImageRead = "userProfileImage.read", UserProfileImageRead = "userProfileImage.read",
UserProfileImageUpdate = "userProfileImage.update", UserProfileImageUpdate = "userProfileImage.update",
UserProfileImageDelete = "userProfileImage.delete", UserProfileImageDelete = "userProfileImage.delete",
QueueRead = "queue.read",
QueueUpdate = "queue.update",
QueueJobCreate = "queueJob.create",
QueueJobRead = "queueJob.read",
QueueJobUpdate = "queueJob.update",
QueueJobDelete = "queueJob.delete",
WorkflowCreate = "workflow.create", WorkflowCreate = "workflow.create",
WorkflowRead = "workflow.read", WorkflowRead = "workflow.read",
WorkflowUpdate = "workflow.update", WorkflowUpdate = "workflow.update",
@ -5330,6 +5423,71 @@ export enum PluginContext {
Album = "album", Album = "album",
Person = "person" Person = "person"
} }
export enum QueueJobStatus {
Active = "active",
Failed = "failed",
Completed = "completed",
Delayed = "delayed",
Waiting = "waiting",
Paused = "paused"
}
export enum JobName {
AssetDelete = "AssetDelete",
AssetDeleteCheck = "AssetDeleteCheck",
AssetDetectFacesQueueAll = "AssetDetectFacesQueueAll",
AssetDetectFaces = "AssetDetectFaces",
AssetDetectDuplicatesQueueAll = "AssetDetectDuplicatesQueueAll",
AssetDetectDuplicates = "AssetDetectDuplicates",
AssetEncodeVideoQueueAll = "AssetEncodeVideoQueueAll",
AssetEncodeVideo = "AssetEncodeVideo",
AssetEmptyTrash = "AssetEmptyTrash",
AssetExtractMetadataQueueAll = "AssetExtractMetadataQueueAll",
AssetExtractMetadata = "AssetExtractMetadata",
AssetFileMigration = "AssetFileMigration",
AssetGenerateThumbnailsQueueAll = "AssetGenerateThumbnailsQueueAll",
AssetGenerateThumbnails = "AssetGenerateThumbnails",
AuditLogCleanup = "AuditLogCleanup",
AuditTableCleanup = "AuditTableCleanup",
DatabaseBackup = "DatabaseBackup",
FacialRecognitionQueueAll = "FacialRecognitionQueueAll",
FacialRecognition = "FacialRecognition",
FileDelete = "FileDelete",
FileMigrationQueueAll = "FileMigrationQueueAll",
LibraryDeleteCheck = "LibraryDeleteCheck",
LibraryDelete = "LibraryDelete",
LibraryRemoveAsset = "LibraryRemoveAsset",
LibraryScanAssetsQueueAll = "LibraryScanAssetsQueueAll",
LibrarySyncAssets = "LibrarySyncAssets",
LibrarySyncFilesQueueAll = "LibrarySyncFilesQueueAll",
LibrarySyncFiles = "LibrarySyncFiles",
LibraryScanQueueAll = "LibraryScanQueueAll",
MemoryCleanup = "MemoryCleanup",
MemoryGenerate = "MemoryGenerate",
NotificationsCleanup = "NotificationsCleanup",
NotifyUserSignup = "NotifyUserSignup",
NotifyAlbumInvite = "NotifyAlbumInvite",
NotifyAlbumUpdate = "NotifyAlbumUpdate",
UserDelete = "UserDelete",
UserDeleteCheck = "UserDeleteCheck",
UserSyncUsage = "UserSyncUsage",
PersonCleanup = "PersonCleanup",
PersonFileMigration = "PersonFileMigration",
PersonGenerateThumbnail = "PersonGenerateThumbnail",
SessionCleanup = "SessionCleanup",
SendMail = "SendMail",
SidecarQueueAll = "SidecarQueueAll",
SidecarCheck = "SidecarCheck",
SidecarWrite = "SidecarWrite",
SmartSearchQueueAll = "SmartSearchQueueAll",
SmartSearch = "SmartSearch",
StorageTemplateMigration = "StorageTemplateMigration",
StorageTemplateMigrationSingle = "StorageTemplateMigrationSingle",
TagCleanup = "TagCleanup",
VersionCheck = "VersionCheck",
OcrQueueAll = "OcrQueueAll",
Ocr = "Ocr",
WorkflowRun = "WorkflowRun"
}
export enum SearchSuggestionType { export enum SearchSuggestionType {
Country = "country", Country = "country",
State = "state", State = "state",

View File

@ -163,6 +163,8 @@ export const endpointTags: Record<ApiTag, string> = {
'A person is a collection of faces, which can be favorited and named. A person can also be merged into another person. People are automatically created via the face recognition job.', 'A person is a collection of faces, which can be favorited and named. A person can also be merged into another person. People are automatically created via the face recognition job.',
[ApiTag.Plugins]: [ApiTag.Plugins]:
'A plugin is an installed module that makes filters and actions available for the workflow feature.', 'A plugin is an installed module that makes filters and actions available for the workflow feature.',
[ApiTag.Queues]:
'Queues and background jobs are used for processing tasks asynchronously. Queues can be paused and resumed as needed.',
[ApiTag.Search]: [ApiTag.Search]:
'Endpoints related to searching assets via text, smart search, optical character recognition (OCR), and other filters like person, album, and other metadata. Search endpoints usually support pagination and sorting.', 'Endpoints related to searching assets via text, smart search, optical character recognition (OCR), and other filters like person, album, and other metadata. Search endpoints usually support pagination and sorting.',
[ApiTag.Server]: [ApiTag.Server]:

View File

@ -20,6 +20,7 @@ import { OAuthController } from 'src/controllers/oauth.controller';
import { PartnerController } from 'src/controllers/partner.controller'; import { PartnerController } from 'src/controllers/partner.controller';
import { PersonController } from 'src/controllers/person.controller'; import { PersonController } from 'src/controllers/person.controller';
import { PluginController } from 'src/controllers/plugin.controller'; import { PluginController } from 'src/controllers/plugin.controller';
import { QueueController } from 'src/controllers/queue.controller';
import { SearchController } from 'src/controllers/search.controller'; import { SearchController } from 'src/controllers/search.controller';
import { ServerController } from 'src/controllers/server.controller'; import { ServerController } from 'src/controllers/server.controller';
import { SessionController } from 'src/controllers/session.controller'; import { SessionController } from 'src/controllers/session.controller';
@ -59,6 +60,7 @@ export const controllers = [
PartnerController, PartnerController,
PersonController, PersonController,
PluginController, PluginController,
QueueController,
SearchController, SearchController,
ServerController, ServerController,
SessionController, SessionController,

View File

@ -1,10 +1,12 @@
import { Body, Controller, Get, HttpCode, HttpStatus, Param, Post, Put } from '@nestjs/common'; import { Body, Controller, Get, HttpCode, HttpStatus, Param, Post, Put } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger'; import { ApiTags } from '@nestjs/swagger';
import { Endpoint, HistoryBuilder } from 'src/decorators'; import { Endpoint, HistoryBuilder } from 'src/decorators';
import { AuthDto } from 'src/dtos/auth.dto';
import { JobCreateDto } from 'src/dtos/job.dto'; import { JobCreateDto } from 'src/dtos/job.dto';
import { QueueCommandDto, QueueNameParamDto, QueueResponseDto, QueuesResponseDto } from 'src/dtos/queue.dto'; import { QueueResponseLegacyDto, QueuesResponseLegacyDto } from 'src/dtos/queue-legacy.dto';
import { QueueCommandDto, QueueNameParamDto } from 'src/dtos/queue.dto';
import { ApiTag, Permission } from 'src/enum'; import { ApiTag, Permission } from 'src/enum';
import { Authenticated } from 'src/middleware/auth.guard'; import { Auth, Authenticated } from 'src/middleware/auth.guard';
import { JobService } from 'src/services/job.service'; import { JobService } from 'src/services/job.service';
import { QueueService } from 'src/services/queue.service'; import { QueueService } from 'src/services/queue.service';
@ -21,10 +23,10 @@ export class JobController {
@Endpoint({ @Endpoint({
summary: 'Retrieve queue counts and status', summary: 'Retrieve queue counts and status',
description: 'Retrieve the counts of the current queue, as well as the current status.', description: 'Retrieve the counts of the current queue, as well as the current status.',
history: new HistoryBuilder().added('v1').beta('v1').stable('v2'), history: new HistoryBuilder().added('v1').beta('v1').stable('v2').deprecated('v2.4.0'),
}) })
getQueuesLegacy(): Promise<QueuesResponseDto> { getQueuesLegacy(@Auth() auth: AuthDto): Promise<QueuesResponseLegacyDto> {
return this.queueService.getAll(); return this.queueService.getAllLegacy(auth);
} }
@Post() @Post()
@ -46,9 +48,12 @@ export class JobController {
summary: 'Run jobs', summary: 'Run jobs',
description: description:
'Queue all assets for a specific job type. Defaults to only queueing assets that have not yet been processed, but the force command can be used to re-process all assets.', 'Queue all assets for a specific job type. Defaults to only queueing assets that have not yet been processed, but the force command can be used to re-process all assets.',
history: new HistoryBuilder().added('v1').beta('v1').stable('v2'), history: new HistoryBuilder().added('v1').beta('v1').stable('v2').deprecated('v2.4.0'),
}) })
runQueueCommandLegacy(@Param() { name }: QueueNameParamDto, @Body() dto: QueueCommandDto): Promise<QueueResponseDto> { runQueueCommandLegacy(
return this.queueService.runCommand(name, dto); @Param() { name }: QueueNameParamDto,
@Body() dto: QueueCommandDto,
): Promise<QueueResponseLegacyDto> {
return this.queueService.runCommandLegacy(name, dto);
} }
} }

View File

@ -0,0 +1,85 @@
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Put, Query } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { Endpoint, HistoryBuilder } from 'src/decorators';
import { AuthDto } from 'src/dtos/auth.dto';
import {
QueueDeleteDto,
QueueJobResponseDto,
QueueJobSearchDto,
QueueNameParamDto,
QueueResponseDto,
QueueUpdateDto,
} from 'src/dtos/queue.dto';
import { ApiTag, Permission } from 'src/enum';
import { Auth, Authenticated } from 'src/middleware/auth.guard';
import { QueueService } from 'src/services/queue.service';
@ApiTags(ApiTag.Queues)
@Controller('queues')
export class QueueController {
constructor(private service: QueueService) {}
@Get()
@Authenticated({ permission: Permission.QueueRead, admin: true })
@Endpoint({
summary: 'List all queues',
description: 'Retrieves a list of queues.',
history: new HistoryBuilder().added('v2.4.0').alpha('v2.4.0'),
})
getQueues(@Auth() auth: AuthDto): Promise<QueueResponseDto[]> {
return this.service.getAll(auth);
}
@Get(':name')
@Authenticated({ permission: Permission.QueueRead, admin: true })
@Endpoint({
summary: 'Retrieve a queue',
description: 'Retrieves a specific queue by its name.',
history: new HistoryBuilder().added('v2.4.0').alpha('v2.4.0'),
})
getQueue(@Auth() auth: AuthDto, @Param() { name }: QueueNameParamDto): Promise<QueueResponseDto> {
return this.service.get(auth, name);
}
@Put(':name')
@Authenticated({ permission: Permission.QueueUpdate, admin: true })
@Endpoint({
summary: 'Update a queue',
description: 'Change the paused status of a specific queue.',
history: new HistoryBuilder().added('v2.4.0').alpha('v2.4.0'),
})
updateQueue(
@Auth() auth: AuthDto,
@Param() { name }: QueueNameParamDto,
@Body() dto: QueueUpdateDto,
): Promise<QueueResponseDto> {
return this.service.update(auth, name, dto);
}
@Get(':name/jobs')
@Authenticated({ permission: Permission.QueueJobRead, admin: true })
@Endpoint({
summary: 'Retrieve queue jobs',
description: 'Retrieves a list of queue jobs from the specified queue.',
history: new HistoryBuilder().added('v2.4.0').alpha('v2.4.0'),
})
getQueueJobs(
@Auth() auth: AuthDto,
@Param() { name }: QueueNameParamDto,
@Query() dto: QueueJobSearchDto,
): Promise<QueueJobResponseDto[]> {
return this.service.searchJobs(auth, name, dto);
}
@Delete(':name/jobs')
@Authenticated({ permission: Permission.QueueJobDelete, admin: true })
@HttpCode(HttpStatus.NO_CONTENT)
@Endpoint({
summary: 'Empty a queue',
description: 'Removes all jobs from the specified queue.',
history: new HistoryBuilder().added('v2.4.0').alpha('v2.4.0'),
})
emptyQueue(@Auth() auth: AuthDto, @Param() { name }: QueueNameParamDto, @Body() dto: QueueDeleteDto): Promise<void> {
return this.service.emptyQueue(auth, name, dto);
}
}

View File

@ -0,0 +1,89 @@
import { ApiProperty } from '@nestjs/swagger';
import { QueueResponseDto, QueueStatisticsDto } from 'src/dtos/queue.dto';
import { QueueName } from 'src/enum';
export class QueueStatusLegacyDto {
isActive!: boolean;
isPaused!: boolean;
}
export class QueueResponseLegacyDto {
@ApiProperty({ type: QueueStatusLegacyDto })
queueStatus!: QueueStatusLegacyDto;
@ApiProperty({ type: QueueStatisticsDto })
jobCounts!: QueueStatisticsDto;
}
export class QueuesResponseLegacyDto implements Record<QueueName, QueueResponseLegacyDto> {
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.ThumbnailGeneration]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.MetadataExtraction]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.VideoConversion]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.SmartSearch]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.StorageTemplateMigration]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.Migration]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.BackgroundTask]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.Search]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.DuplicateDetection]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.FaceDetection]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.FacialRecognition]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.Sidecar]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.Library]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.Notification]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.BackupDatabase]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.Ocr]!: QueueResponseLegacyDto;
@ApiProperty({ type: QueueResponseLegacyDto })
[QueueName.Workflow]!: QueueResponseLegacyDto;
}
export const mapQueueLegacy = (response: QueueResponseDto): QueueResponseLegacyDto => {
return {
queueStatus: {
isPaused: response.isPaused,
isActive: response.statistics.active > 0,
},
jobCounts: response.statistics,
};
};
export const mapQueuesLegacy = (responses: QueueResponseDto[]): QueuesResponseLegacyDto => {
const legacy = new QueuesResponseLegacyDto();
for (const response of responses) {
legacy[response.name] = mapQueueLegacy(response);
}
return legacy;
};

View File

@ -1,5 +1,6 @@
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
import { QueueCommand, QueueName } from 'src/enum'; import { HistoryBuilder, Property } from 'src/decorators';
import { JobName, QueueCommand, QueueJobStatus, QueueName } from 'src/enum';
import { ValidateBoolean, ValidateEnum } from 'src/validation'; import { ValidateBoolean, ValidateEnum } from 'src/validation';
export class QueueNameParamDto { export class QueueNameParamDto {
@ -15,6 +16,46 @@ export class QueueCommandDto {
force?: boolean; // TODO: this uses undefined as a third state, which should be refactored to be more explicit force?: boolean; // TODO: this uses undefined as a third state, which should be refactored to be more explicit
} }
export class QueueUpdateDto {
@ValidateBoolean({ optional: true })
isPaused?: boolean;
}
export class QueueDeleteDto {
@ValidateBoolean({ optional: true })
@Property({
description: 'If true, will also remove failed jobs from the queue.',
history: new HistoryBuilder().added('v2.4.0').alpha('v2.4.0'),
})
failed?: boolean;
}
export class QueueJobSearchDto {
@ValidateEnum({ enum: QueueJobStatus, name: 'QueueJobStatus', optional: true, each: true })
status?: QueueJobStatus[];
}
export class QueueJobResponseDto {
id?: string;
@ValidateEnum({ enum: JobName, name: 'JobName' })
name!: JobName;
data!: object;
@ApiProperty({ type: 'integer' })
timestamp!: number;
}
export class QueueResponseDto {
@ValidateEnum({ enum: QueueName, name: 'QueueName' })
name!: QueueName;
@ValidateBoolean()
isPaused!: boolean;
statistics!: QueueStatisticsDto;
}
export class QueueStatisticsDto { export class QueueStatisticsDto {
@ApiProperty({ type: 'integer' }) @ApiProperty({ type: 'integer' })
active!: number; active!: number;
@ -29,69 +70,3 @@ export class QueueStatisticsDto {
@ApiProperty({ type: 'integer' }) @ApiProperty({ type: 'integer' })
paused!: number; paused!: number;
} }
export class QueueStatusDto {
isActive!: boolean;
isPaused!: boolean;
}
export class QueueResponseDto {
@ApiProperty({ type: QueueStatisticsDto })
jobCounts!: QueueStatisticsDto;
@ApiProperty({ type: QueueStatusDto })
queueStatus!: QueueStatusDto;
}
export class QueuesResponseDto implements Record<QueueName, QueueResponseDto> {
@ApiProperty({ type: QueueResponseDto })
[QueueName.ThumbnailGeneration]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.MetadataExtraction]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.VideoConversion]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.SmartSearch]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.StorageTemplateMigration]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.Migration]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.BackgroundTask]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.Search]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.DuplicateDetection]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.FaceDetection]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.FacialRecognition]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.Sidecar]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.Library]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.Notification]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.BackupDatabase]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.Ocr]!: QueueResponseDto;
@ApiProperty({ type: QueueResponseDto })
[QueueName.Workflow]!: QueueResponseDto;
}

View File

@ -248,6 +248,14 @@ export enum Permission {
UserProfileImageUpdate = 'userProfileImage.update', UserProfileImageUpdate = 'userProfileImage.update',
UserProfileImageDelete = 'userProfileImage.delete', UserProfileImageDelete = 'userProfileImage.delete',
QueueRead = 'queue.read',
QueueUpdate = 'queue.update',
QueueJobCreate = 'queueJob.create',
QueueJobRead = 'queueJob.read',
QueueJobUpdate = 'queueJob.update',
QueueJobDelete = 'queueJob.delete',
WorkflowCreate = 'workflow.create', WorkflowCreate = 'workflow.create',
WorkflowRead = 'workflow.read', WorkflowRead = 'workflow.read',
WorkflowUpdate = 'workflow.update', WorkflowUpdate = 'workflow.update',
@ -543,6 +551,15 @@ export enum QueueName {
Workflow = 'workflow', Workflow = 'workflow',
} }
export enum QueueJobStatus {
Active = 'active',
Failed = 'failed',
Complete = 'completed',
Delayed = 'delayed',
Waiting = 'waiting',
Paused = 'paused',
}
export enum JobName { export enum JobName {
AssetDelete = 'AssetDelete', AssetDelete = 'AssetDelete',
AssetDeleteCheck = 'AssetDeleteCheck', AssetDeleteCheck = 'AssetDeleteCheck',
@ -624,9 +641,13 @@ export enum JobName {
export enum QueueCommand { export enum QueueCommand {
Start = 'start', Start = 'start',
/** @deprecated Use `updateQueue` instead */
Pause = 'pause', Pause = 'pause',
/** @deprecated Use `updateQueue` instead */
Resume = 'resume', Resume = 'resume',
/** @deprecated Use `emptyQueue` instead */
Empty = 'empty', Empty = 'empty',
/** @deprecated Use `emptyQueue` instead */
ClearFailed = 'clear-failed', ClearFailed = 'clear-failed',
} }
@ -823,6 +844,7 @@ export enum ApiTag {
Partners = 'Partners', Partners = 'Partners',
People = 'People', People = 'People',
Plugins = 'Plugins', Plugins = 'Plugins',
Queues = 'Queues',
Search = 'Search', Search = 'Search',
Server = 'Server', Server = 'Server',
Sessions = 'Sessions', Sessions = 'Sessions',

View File

@ -249,7 +249,7 @@ const getEnv = (): EnvData => {
prefix: 'immich_bull', prefix: 'immich_bull',
connection: { ...redisConfig }, connection: { ...redisConfig },
defaultJobOptions: { defaultJobOptions: {
attempts: 3, attempts: 1,
removeOnComplete: true, removeOnComplete: true,
removeOnFail: false, removeOnFail: false,
}, },

View File

@ -5,11 +5,12 @@ import { JobsOptions, Queue, Worker } from 'bullmq';
import { ClassConstructor } from 'class-transformer'; import { ClassConstructor } from 'class-transformer';
import { setTimeout } from 'node:timers/promises'; import { setTimeout } from 'node:timers/promises';
import { JobConfig } from 'src/decorators'; import { JobConfig } from 'src/decorators';
import { JobName, JobStatus, MetadataKey, QueueCleanType, QueueName } from 'src/enum'; import { QueueJobResponseDto, QueueJobSearchDto } from 'src/dtos/queue.dto';
import { JobName, JobStatus, MetadataKey, QueueCleanType, QueueJobStatus, QueueName } from 'src/enum';
import { ConfigRepository } from 'src/repositories/config.repository'; import { ConfigRepository } from 'src/repositories/config.repository';
import { EventRepository } from 'src/repositories/event.repository'; import { EventRepository } from 'src/repositories/event.repository';
import { LoggingRepository } from 'src/repositories/logging.repository'; import { LoggingRepository } from 'src/repositories/logging.repository';
import { JobCounts, JobItem, JobOf, QueueStatus } from 'src/types'; import { JobCounts, JobItem, JobOf } from 'src/types';
import { getKeyByValue, getMethodNames, ImmichStartupError } from 'src/utils/misc'; import { getKeyByValue, getMethodNames, ImmichStartupError } from 'src/utils/misc';
type JobMapItem = { type JobMapItem = {
@ -115,13 +116,14 @@ export class JobRepository {
worker.concurrency = concurrency; worker.concurrency = concurrency;
} }
async getQueueStatus(name: QueueName): Promise<QueueStatus> { async isActive(name: QueueName): Promise<boolean> {
const queue = this.getQueue(name); const queue = this.getQueue(name);
const count = await queue.getActiveCount();
return count > 0;
}
return { async isPaused(name: QueueName): Promise<boolean> {
isActive: !!(await queue.getActiveCount()), return this.getQueue(name).isPaused();
isPaused: await queue.isPaused(),
};
} }
pause(name: QueueName) { pause(name: QueueName) {
@ -192,17 +194,28 @@ export class JobRepository {
} }
async waitForQueueCompletion(...queues: QueueName[]): Promise<void> { async waitForQueueCompletion(...queues: QueueName[]): Promise<void> {
let activeQueue: QueueStatus | undefined; const getPending = async () => {
do { const results = await Promise.all(queues.map(async (name) => ({ pending: await this.isActive(name), name })));
const statuses = await Promise.all(queues.map((name) => this.getQueueStatus(name))); return results.filter(({ pending }) => pending).map(({ name }) => name);
activeQueue = statuses.find((status) => status.isActive); };
} while (activeQueue);
{ let pending = await getPending();
this.logger.verbose(`Waiting for ${activeQueue} queue to stop...`);
while (pending.length > 0) {
this.logger.verbose(`Waiting for ${pending[0]} queue to stop...`);
await setTimeout(1000); await setTimeout(1000);
pending = await getPending();
} }
} }
async searchJobs(name: QueueName, dto: QueueJobSearchDto): Promise<QueueJobResponseDto[]> {
const jobs = await this.getQueue(name).getJobs(dto.status ?? Object.values(QueueJobStatus), 0, 1000);
return jobs.map((job) => {
const { id, name, timestamp, data } = job;
return { id, name: name as JobName, timestamp, data };
});
}
private getJobOptions(item: JobItem): JobsOptions | null { private getJobOptions(item: JobItem): JobsOptions | null {
switch (item.name) { switch (item.name) {
case JobName.NotifyAlbumUpdate: { case JobName.NotifyAlbumUpdate: {

View File

@ -2,6 +2,7 @@ import { BadRequestException } from '@nestjs/common';
import { defaults, SystemConfig } from 'src/config'; import { defaults, SystemConfig } from 'src/config';
import { ImmichWorker, JobName, QueueCommand, QueueName } from 'src/enum'; import { ImmichWorker, JobName, QueueCommand, QueueName } from 'src/enum';
import { QueueService } from 'src/services/queue.service'; import { QueueService } from 'src/services/queue.service';
import { factory } from 'test/small.factory';
import { newTestService, ServiceMocks } from 'test/utils'; import { newTestService, ServiceMocks } from 'test/utils';
describe(QueueService.name, () => { describe(QueueService.name, () => {
@ -52,80 +53,64 @@ describe(QueueService.name, () => {
describe('getAllJobStatus', () => { describe('getAllJobStatus', () => {
it('should get all job statuses', async () => { it('should get all job statuses', async () => {
mocks.job.getJobCounts.mockResolvedValue({ const stats = factory.queueStatistics({ active: 1 });
active: 1, const expected = { jobCounts: stats, queueStatus: { isActive: true, isPaused: true } };
completed: 1,
failed: 1,
delayed: 1,
waiting: 1,
paused: 1,
});
mocks.job.getQueueStatus.mockResolvedValue({
isActive: true,
isPaused: true,
});
const expectedJobStatus = { mocks.job.getJobCounts.mockResolvedValue(stats);
jobCounts: { mocks.job.isPaused.mockResolvedValue(true);
active: 1,
completed: 1,
delayed: 1,
failed: 1,
waiting: 1,
paused: 1,
},
queueStatus: {
isActive: true,
isPaused: true,
},
};
await expect(sut.getAll()).resolves.toEqual({ await expect(sut.getAllLegacy(factory.auth())).resolves.toEqual({
[QueueName.BackgroundTask]: expectedJobStatus, [QueueName.BackgroundTask]: expected,
[QueueName.DuplicateDetection]: expectedJobStatus, [QueueName.DuplicateDetection]: expected,
[QueueName.SmartSearch]: expectedJobStatus, [QueueName.SmartSearch]: expected,
[QueueName.MetadataExtraction]: expectedJobStatus, [QueueName.MetadataExtraction]: expected,
[QueueName.Search]: expectedJobStatus, [QueueName.Search]: expected,
[QueueName.StorageTemplateMigration]: expectedJobStatus, [QueueName.StorageTemplateMigration]: expected,
[QueueName.Migration]: expectedJobStatus, [QueueName.Migration]: expected,
[QueueName.ThumbnailGeneration]: expectedJobStatus, [QueueName.ThumbnailGeneration]: expected,
[QueueName.VideoConversion]: expectedJobStatus, [QueueName.VideoConversion]: expected,
[QueueName.FaceDetection]: expectedJobStatus, [QueueName.FaceDetection]: expected,
[QueueName.FacialRecognition]: expectedJobStatus, [QueueName.FacialRecognition]: expected,
[QueueName.Sidecar]: expectedJobStatus, [QueueName.Sidecar]: expected,
[QueueName.Library]: expectedJobStatus, [QueueName.Library]: expected,
[QueueName.Notification]: expectedJobStatus, [QueueName.Notification]: expected,
[QueueName.BackupDatabase]: expectedJobStatus, [QueueName.BackupDatabase]: expected,
[QueueName.Ocr]: expectedJobStatus, [QueueName.Ocr]: expected,
[QueueName.Workflow]: expectedJobStatus, [QueueName.Workflow]: expected,
}); });
}); });
}); });
describe('handleCommand', () => { describe('handleCommand', () => {
it('should handle a pause command', async () => { it('should handle a pause command', async () => {
await sut.runCommand(QueueName.MetadataExtraction, { command: QueueCommand.Pause, force: false }); mocks.job.getJobCounts.mockResolvedValue(factory.queueStatistics());
await sut.runCommandLegacy(QueueName.MetadataExtraction, { command: QueueCommand.Pause, force: false });
expect(mocks.job.pause).toHaveBeenCalledWith(QueueName.MetadataExtraction); expect(mocks.job.pause).toHaveBeenCalledWith(QueueName.MetadataExtraction);
}); });
it('should handle a resume command', async () => { it('should handle a resume command', async () => {
await sut.runCommand(QueueName.MetadataExtraction, { command: QueueCommand.Resume, force: false }); mocks.job.getJobCounts.mockResolvedValue(factory.queueStatistics());
await sut.runCommandLegacy(QueueName.MetadataExtraction, { command: QueueCommand.Resume, force: false });
expect(mocks.job.resume).toHaveBeenCalledWith(QueueName.MetadataExtraction); expect(mocks.job.resume).toHaveBeenCalledWith(QueueName.MetadataExtraction);
}); });
it('should handle an empty command', async () => { it('should handle an empty command', async () => {
await sut.runCommand(QueueName.MetadataExtraction, { command: QueueCommand.Empty, force: false }); mocks.job.getJobCounts.mockResolvedValue(factory.queueStatistics());
await sut.runCommandLegacy(QueueName.MetadataExtraction, { command: QueueCommand.Empty, force: false });
expect(mocks.job.empty).toHaveBeenCalledWith(QueueName.MetadataExtraction); expect(mocks.job.empty).toHaveBeenCalledWith(QueueName.MetadataExtraction);
}); });
it('should not start a job that is already running', async () => { it('should not start a job that is already running', async () => {
mocks.job.getQueueStatus.mockResolvedValue({ isActive: true, isPaused: false }); mocks.job.isActive.mockResolvedValue(true);
await expect( await expect(
sut.runCommand(QueueName.VideoConversion, { command: QueueCommand.Start, force: false }), sut.runCommandLegacy(QueueName.VideoConversion, { command: QueueCommand.Start, force: false }),
).rejects.toBeInstanceOf(BadRequestException); ).rejects.toBeInstanceOf(BadRequestException);
expect(mocks.job.queue).not.toHaveBeenCalled(); expect(mocks.job.queue).not.toHaveBeenCalled();
@ -133,33 +118,37 @@ describe(QueueService.name, () => {
}); });
it('should handle a start video conversion command', async () => { it('should handle a start video conversion command', async () => {
mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); mocks.job.isActive.mockResolvedValue(false);
mocks.job.getJobCounts.mockResolvedValue(factory.queueStatistics());
await sut.runCommand(QueueName.VideoConversion, { command: QueueCommand.Start, force: false }); await sut.runCommandLegacy(QueueName.VideoConversion, { command: QueueCommand.Start, force: false });
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.AssetEncodeVideoQueueAll, data: { force: false } }); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.AssetEncodeVideoQueueAll, data: { force: false } });
}); });
it('should handle a start storage template migration command', async () => { it('should handle a start storage template migration command', async () => {
mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); mocks.job.isActive.mockResolvedValue(false);
mocks.job.getJobCounts.mockResolvedValue(factory.queueStatistics());
await sut.runCommand(QueueName.StorageTemplateMigration, { command: QueueCommand.Start, force: false }); await sut.runCommandLegacy(QueueName.StorageTemplateMigration, { command: QueueCommand.Start, force: false });
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.StorageTemplateMigration }); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.StorageTemplateMigration });
}); });
it('should handle a start smart search command', async () => { it('should handle a start smart search command', async () => {
mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); mocks.job.isActive.mockResolvedValue(false);
mocks.job.getJobCounts.mockResolvedValue(factory.queueStatistics());
await sut.runCommand(QueueName.SmartSearch, { command: QueueCommand.Start, force: false }); await sut.runCommandLegacy(QueueName.SmartSearch, { command: QueueCommand.Start, force: false });
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.SmartSearchQueueAll, data: { force: false } }); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.SmartSearchQueueAll, data: { force: false } });
}); });
it('should handle a start metadata extraction command', async () => { it('should handle a start metadata extraction command', async () => {
mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); mocks.job.isActive.mockResolvedValue(false);
mocks.job.getJobCounts.mockResolvedValue(factory.queueStatistics());
await sut.runCommand(QueueName.MetadataExtraction, { command: QueueCommand.Start, force: false }); await sut.runCommandLegacy(QueueName.MetadataExtraction, { command: QueueCommand.Start, force: false });
expect(mocks.job.queue).toHaveBeenCalledWith({ expect(mocks.job.queue).toHaveBeenCalledWith({
name: JobName.AssetExtractMetadataQueueAll, name: JobName.AssetExtractMetadataQueueAll,
@ -168,17 +157,19 @@ describe(QueueService.name, () => {
}); });
it('should handle a start sidecar command', async () => { it('should handle a start sidecar command', async () => {
mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); mocks.job.isActive.mockResolvedValue(false);
mocks.job.getJobCounts.mockResolvedValue(factory.queueStatistics());
await sut.runCommand(QueueName.Sidecar, { command: QueueCommand.Start, force: false }); await sut.runCommandLegacy(QueueName.Sidecar, { command: QueueCommand.Start, force: false });
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.SidecarQueueAll, data: { force: false } }); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.SidecarQueueAll, data: { force: false } });
}); });
it('should handle a start thumbnail generation command', async () => { it('should handle a start thumbnail generation command', async () => {
mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); mocks.job.isActive.mockResolvedValue(false);
mocks.job.getJobCounts.mockResolvedValue(factory.queueStatistics());
await sut.runCommand(QueueName.ThumbnailGeneration, { command: QueueCommand.Start, force: false }); await sut.runCommandLegacy(QueueName.ThumbnailGeneration, { command: QueueCommand.Start, force: false });
expect(mocks.job.queue).toHaveBeenCalledWith({ expect(mocks.job.queue).toHaveBeenCalledWith({
name: JobName.AssetGenerateThumbnailsQueueAll, name: JobName.AssetGenerateThumbnailsQueueAll,
@ -187,34 +178,37 @@ describe(QueueService.name, () => {
}); });
it('should handle a start face detection command', async () => { it('should handle a start face detection command', async () => {
mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); mocks.job.isActive.mockResolvedValue(false);
mocks.job.getJobCounts.mockResolvedValue(factory.queueStatistics());
await sut.runCommand(QueueName.FaceDetection, { command: QueueCommand.Start, force: false }); await sut.runCommandLegacy(QueueName.FaceDetection, { command: QueueCommand.Start, force: false });
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.AssetDetectFacesQueueAll, data: { force: false } }); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.AssetDetectFacesQueueAll, data: { force: false } });
}); });
it('should handle a start facial recognition command', async () => { it('should handle a start facial recognition command', async () => {
mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); mocks.job.isActive.mockResolvedValue(false);
mocks.job.getJobCounts.mockResolvedValue(factory.queueStatistics());
await sut.runCommand(QueueName.FacialRecognition, { command: QueueCommand.Start, force: false }); await sut.runCommandLegacy(QueueName.FacialRecognition, { command: QueueCommand.Start, force: false });
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.FacialRecognitionQueueAll, data: { force: false } }); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.FacialRecognitionQueueAll, data: { force: false } });
}); });
it('should handle a start backup database command', async () => { it('should handle a start backup database command', async () => {
mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); mocks.job.isActive.mockResolvedValue(false);
mocks.job.getJobCounts.mockResolvedValue(factory.queueStatistics());
await sut.runCommand(QueueName.BackupDatabase, { command: QueueCommand.Start, force: false }); await sut.runCommandLegacy(QueueName.BackupDatabase, { command: QueueCommand.Start, force: false });
expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.DatabaseBackup, data: { force: false } }); expect(mocks.job.queue).toHaveBeenCalledWith({ name: JobName.DatabaseBackup, data: { force: false } });
}); });
it('should throw a bad request when an invalid queue is used', async () => { it('should throw a bad request when an invalid queue is used', async () => {
mocks.job.getQueueStatus.mockResolvedValue({ isActive: false, isPaused: false }); mocks.job.isActive.mockResolvedValue(false);
await expect( await expect(
sut.runCommand(QueueName.BackgroundTask, { command: QueueCommand.Start, force: false }), sut.runCommandLegacy(QueueName.BackgroundTask, { command: QueueCommand.Start, force: false }),
).rejects.toBeInstanceOf(BadRequestException); ).rejects.toBeInstanceOf(BadRequestException);
expect(mocks.job.queue).not.toHaveBeenCalled(); expect(mocks.job.queue).not.toHaveBeenCalled();

View File

@ -2,7 +2,21 @@ import { BadRequestException, Injectable } from '@nestjs/common';
import { ClassConstructor } from 'class-transformer'; import { ClassConstructor } from 'class-transformer';
import { SystemConfig } from 'src/config'; import { SystemConfig } from 'src/config';
import { OnEvent } from 'src/decorators'; import { OnEvent } from 'src/decorators';
import { QueueCommandDto, QueueResponseDto, QueuesResponseDto } from 'src/dtos/queue.dto'; import { AuthDto } from 'src/dtos/auth.dto';
import {
mapQueueLegacy,
mapQueuesLegacy,
QueueResponseLegacyDto,
QueuesResponseLegacyDto,
} from 'src/dtos/queue-legacy.dto';
import {
QueueCommandDto,
QueueDeleteDto,
QueueJobResponseDto,
QueueJobSearchDto,
QueueResponseDto,
QueueUpdateDto,
} from 'src/dtos/queue.dto';
import { import {
BootstrapEventPriority, BootstrapEventPriority,
CronJob, CronJob,
@ -86,7 +100,7 @@ export class QueueService extends BaseService {
this.services = services; this.services = services;
} }
async runCommand(name: QueueName, dto: QueueCommandDto): Promise<QueueResponseDto> { async runCommandLegacy(name: QueueName, dto: QueueCommandDto): Promise<QueueResponseLegacyDto> {
this.logger.debug(`Handling command: queue=${name},command=${dto.command},force=${dto.force}`); this.logger.debug(`Handling command: queue=${name},command=${dto.command},force=${dto.force}`);
switch (dto.command) { switch (dto.command) {
@ -117,28 +131,60 @@ export class QueueService extends BaseService {
} }
} }
const response = await this.getByName(name);
return mapQueueLegacy(response);
}
async getAll(_auth: AuthDto): Promise<QueueResponseDto[]> {
return Promise.all(Object.values(QueueName).map((name) => this.getByName(name)));
}
async getAllLegacy(auth: AuthDto): Promise<QueuesResponseLegacyDto> {
const responses = await this.getAll(auth);
return mapQueuesLegacy(responses);
}
get(auth: AuthDto, name: QueueName): Promise<QueueResponseDto> {
return this.getByName(name); return this.getByName(name);
} }
async getAll(): Promise<QueuesResponseDto> { async update(auth: AuthDto, name: QueueName, dto: QueueUpdateDto): Promise<QueueResponseDto> {
const response = new QueuesResponseDto(); if (dto.isPaused === true) {
for (const name of Object.values(QueueName)) { if (name === QueueName.BackgroundTask) {
response[name] = await this.getByName(name); throw new BadRequestException(`The BackgroundTask queue cannot be paused`);
}
await this.jobRepository.pause(name);
} }
return response;
if (dto.isPaused === false) {
await this.jobRepository.resume(name);
}
return this.getByName(name);
} }
async getByName(name: QueueName): Promise<QueueResponseDto> { searchJobs(auth: AuthDto, name: QueueName, dto: QueueJobSearchDto): Promise<QueueJobResponseDto[]> {
const [jobCounts, queueStatus] = await Promise.all([ return this.jobRepository.searchJobs(name, dto);
this.jobRepository.getJobCounts(name), }
this.jobRepository.getQueueStatus(name),
]);
return { jobCounts, queueStatus }; async emptyQueue(auth: AuthDto, name: QueueName, dto: QueueDeleteDto) {
await this.jobRepository.empty(name);
if (dto.failed) {
await this.jobRepository.clear(name, QueueCleanType.Failed);
}
}
private async getByName(name: QueueName): Promise<QueueResponseDto> {
const [statistics, isPaused] = await Promise.all([
this.jobRepository.getJobCounts(name),
this.jobRepository.isPaused(name),
]);
return { name, isPaused, statistics };
} }
private async start(name: QueueName, { force }: QueueCommandDto): Promise<void> { private async start(name: QueueName, { force }: QueueCommandDto): Promise<void> {
const { isActive } = await this.jobRepository.getQueueStatus(name); const isActive = await this.jobRepository.isActive(name);
if (isActive) { if (isActive) {
throw new BadRequestException(`Job is already running`); throw new BadRequestException(`Job is already running`);
} }

View File

@ -291,11 +291,6 @@ export interface JobCounts {
paused: number; paused: number;
} }
export interface QueueStatus {
isActive: boolean;
isPaused: boolean;
}
export type JobItem = export type JobItem =
// Audit // Audit
| { name: JobName.AuditTableCleanup; data?: IBaseJob } | { name: JobName.AuditTableCleanup; data?: IBaseJob }

View File

@ -11,9 +11,11 @@ export const newJobRepositoryMock = (): Mocked<RepositoryInterface<JobRepository
empty: vitest.fn(), empty: vitest.fn(),
pause: vitest.fn(), pause: vitest.fn(),
resume: vitest.fn(), resume: vitest.fn(),
searchJobs: vitest.fn(),
queue: vitest.fn().mockImplementation(() => Promise.resolve()), queue: vitest.fn().mockImplementation(() => Promise.resolve()),
queueAll: vitest.fn().mockImplementation(() => Promise.resolve()), queueAll: vitest.fn().mockImplementation(() => Promise.resolve()),
getQueueStatus: vitest.fn(), isActive: vitest.fn(),
isPaused: vitest.fn(),
getJobCounts: vitest.fn(), getJobCounts: vitest.fn(),
clear: vitest.fn(), clear: vitest.fn(),
waitForQueueCompletion: vitest.fn(), waitForQueueCompletion: vitest.fn(),

View File

@ -14,6 +14,7 @@ import {
} from 'src/database'; } from 'src/database';
import { MapAsset } from 'src/dtos/asset-response.dto'; import { MapAsset } from 'src/dtos/asset-response.dto';
import { AuthDto } from 'src/dtos/auth.dto'; import { AuthDto } from 'src/dtos/auth.dto';
import { QueueStatisticsDto } from 'src/dtos/queue.dto';
import { AssetStatus, AssetType, AssetVisibility, MemoryType, Permission, UserMetadataKey, UserStatus } from 'src/enum'; import { AssetStatus, AssetType, AssetVisibility, MemoryType, Permission, UserMetadataKey, UserStatus } from 'src/enum';
import { OnThisDayData, UserMetadataItem } from 'src/types'; import { OnThisDayData, UserMetadataItem } from 'src/types';
import { v4, v7 } from 'uuid'; import { v4, v7 } from 'uuid';
@ -139,6 +140,16 @@ const sessionFactory = (session: Partial<Session> = {}) => ({
...session, ...session,
}); });
const queueStatisticsFactory = (dto?: Partial<QueueStatisticsDto>) => ({
active: 0,
completed: 0,
failed: 0,
delayed: 0,
waiting: 0,
paused: 0,
...dto,
});
const stackFactory = () => ({ const stackFactory = () => ({
id: newUuid(), id: newUuid(),
ownerId: newUuid(), ownerId: newUuid(),
@ -353,6 +364,7 @@ export const factory = {
library: libraryFactory, library: libraryFactory,
memory: memoryFactory, memory: memoryFactory,
partner: partnerFactory, partner: partnerFactory,
queueStatistics: queueStatisticsFactory,
session: sessionFactory, session: sessionFactory,
stack: stackFactory, stack: stackFactory,
user: userFactory, user: userFactory,

View File

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import Badge from '$lib/elements/Badge.svelte'; import Badge from '$lib/elements/Badge.svelte';
import { locale } from '$lib/stores/preferences.store'; import { locale } from '$lib/stores/preferences.store';
import { QueueCommand, type QueueCommandDto, type QueueStatisticsDto, type QueueStatusDto } from '@immich/sdk'; import { QueueCommand, type QueueCommandDto, type QueueStatisticsDto, type QueueStatusLegacyDto } from '@immich/sdk';
import { Icon, IconButton } from '@immich/ui'; import { Icon, IconButton } from '@immich/ui';
import { import {
mdiAlertCircle, mdiAlertCircle,
@ -23,7 +23,7 @@
subtitle: string | undefined; subtitle: string | undefined;
description: Component | undefined; description: Component | undefined;
statistics: QueueStatisticsDto; statistics: QueueStatisticsDto;
queueStatus: QueueStatusDto; queueStatus: QueueStatusLegacyDto;
icon: string; icon: string;
disabled?: boolean; disabled?: boolean;
allText: string | undefined; allText: string | undefined;

View File

@ -6,7 +6,7 @@
QueueCommand, QueueCommand,
type QueueCommandDto, type QueueCommandDto,
QueueName, QueueName,
type QueuesResponseDto, type QueuesResponseLegacyDto,
runQueueCommandLegacy, runQueueCommandLegacy,
} from '@immich/sdk'; } from '@immich/sdk';
import { modalManager, toastManager } from '@immich/ui'; import { modalManager, toastManager } from '@immich/ui';
@ -29,7 +29,7 @@
import StorageMigrationDescription from './StorageMigrationDescription.svelte'; import StorageMigrationDescription from './StorageMigrationDescription.svelte';
interface Props { interface Props {
jobs: QueuesResponseDto; jobs: QueuesResponseLegacyDto;
} }
let { jobs = $bindable() }: Props = $props(); let { jobs = $bindable() }: Props = $props();

View File

@ -5,7 +5,13 @@
import JobCreateModal from '$lib/modals/JobCreateModal.svelte'; import JobCreateModal from '$lib/modals/JobCreateModal.svelte';
import { asyncTimeout } from '$lib/utils'; import { asyncTimeout } from '$lib/utils';
import { handleError } from '$lib/utils/handle-error'; import { handleError } from '$lib/utils/handle-error';
import { getQueuesLegacy, QueueCommand, QueueName, runQueueCommandLegacy, type QueuesResponseDto } from '@immich/sdk'; import {
getQueuesLegacy,
QueueCommand,
QueueName,
runQueueCommandLegacy,
type QueuesResponseLegacyDto,
} from '@immich/sdk';
import { Button, HStack, modalManager, Text } from '@immich/ui'; import { Button, HStack, modalManager, Text } from '@immich/ui';
import { mdiCog, mdiPlay, mdiPlus } from '@mdi/js'; import { mdiCog, mdiPlay, mdiPlus } from '@mdi/js';
import { onDestroy, onMount } from 'svelte'; import { onDestroy, onMount } from 'svelte';
@ -18,7 +24,7 @@
let { data }: Props = $props(); let { data }: Props = $props();
let jobs: QueuesResponseDto | undefined = $state(); let jobs: QueuesResponseLegacyDto | undefined = $state();
let running = true; let running = true;