mirror of
https://github.com/immich-app/immich.git
synced 2026-03-23 07:09:10 +03:00
feat(server): ocr sync
This commit is contained in:
1
mobile/openapi/README.md
generated
1
mobile/openapi/README.md
generated
@@ -582,6 +582,7 @@ Class | Method | HTTP request | Description
|
||||
- [SyncAssetFaceV2](doc//SyncAssetFaceV2.md)
|
||||
- [SyncAssetMetadataDeleteV1](doc//SyncAssetMetadataDeleteV1.md)
|
||||
- [SyncAssetMetadataV1](doc//SyncAssetMetadataV1.md)
|
||||
- [SyncAssetOcrV1](doc//SyncAssetOcrV1.md)
|
||||
- [SyncAssetV1](doc//SyncAssetV1.md)
|
||||
- [SyncAuthUserV1](doc//SyncAuthUserV1.md)
|
||||
- [SyncEntityType](doc//SyncEntityType.md)
|
||||
|
||||
1
mobile/openapi/lib/api.dart
generated
1
mobile/openapi/lib/api.dart
generated
@@ -321,6 +321,7 @@ part 'model/sync_asset_face_v1.dart';
|
||||
part 'model/sync_asset_face_v2.dart';
|
||||
part 'model/sync_asset_metadata_delete_v1.dart';
|
||||
part 'model/sync_asset_metadata_v1.dart';
|
||||
part 'model/sync_asset_ocr_v1.dart';
|
||||
part 'model/sync_asset_v1.dart';
|
||||
part 'model/sync_auth_user_v1.dart';
|
||||
part 'model/sync_entity_type.dart';
|
||||
|
||||
2
mobile/openapi/lib/api_client.dart
generated
2
mobile/openapi/lib/api_client.dart
generated
@@ -688,6 +688,8 @@ class ApiClient {
|
||||
return SyncAssetMetadataDeleteV1.fromJson(value);
|
||||
case 'SyncAssetMetadataV1':
|
||||
return SyncAssetMetadataV1.fromJson(value);
|
||||
case 'SyncAssetOcrV1':
|
||||
return SyncAssetOcrV1.fromJson(value);
|
||||
case 'SyncAssetV1':
|
||||
return SyncAssetV1.fromJson(value);
|
||||
case 'SyncAuthUserV1':
|
||||
|
||||
217
mobile/openapi/lib/model/sync_asset_ocr_v1.dart
generated
Normal file
217
mobile/openapi/lib/model/sync_asset_ocr_v1.dart
generated
Normal file
@@ -0,0 +1,217 @@
|
||||
//
|
||||
// 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 SyncAssetOcrV1 {
|
||||
/// Returns a new [SyncAssetOcrV1] instance.
|
||||
SyncAssetOcrV1({
|
||||
required this.assetId,
|
||||
required this.boxScore,
|
||||
required this.id,
|
||||
required this.isVisible,
|
||||
required this.text,
|
||||
required this.textScore,
|
||||
required this.x1,
|
||||
required this.x2,
|
||||
required this.x3,
|
||||
required this.x4,
|
||||
required this.y1,
|
||||
required this.y2,
|
||||
required this.y3,
|
||||
required this.y4,
|
||||
});
|
||||
|
||||
/// Asset ID
|
||||
String assetId;
|
||||
|
||||
/// Confidence score of the bounding box
|
||||
num boxScore;
|
||||
|
||||
/// OCR entry ID
|
||||
String id;
|
||||
|
||||
/// Whether the OCR entry is visible
|
||||
bool isVisible;
|
||||
|
||||
/// Recognized text content
|
||||
String text;
|
||||
|
||||
/// Confidence score of the recognized text
|
||||
num textScore;
|
||||
|
||||
/// Top-left X coordinate (normalized 0–1)
|
||||
num x1;
|
||||
|
||||
/// Top-right X coordinate (normalized 0–1)
|
||||
num x2;
|
||||
|
||||
/// Bottom-right X coordinate (normalized 0–1)
|
||||
num x3;
|
||||
|
||||
/// Bottom-left X coordinate (normalized 0–1)
|
||||
num x4;
|
||||
|
||||
/// Top-left Y coordinate (normalized 0–1)
|
||||
num y1;
|
||||
|
||||
/// Top-right Y coordinate (normalized 0–1)
|
||||
num y2;
|
||||
|
||||
/// Bottom-right Y coordinate (normalized 0–1)
|
||||
num y3;
|
||||
|
||||
/// Bottom-left Y coordinate (normalized 0–1)
|
||||
num y4;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) => identical(this, other) || other is SyncAssetOcrV1 &&
|
||||
other.assetId == assetId &&
|
||||
other.boxScore == boxScore &&
|
||||
other.id == id &&
|
||||
other.isVisible == isVisible &&
|
||||
other.text == text &&
|
||||
other.textScore == textScore &&
|
||||
other.x1 == x1 &&
|
||||
other.x2 == x2 &&
|
||||
other.x3 == x3 &&
|
||||
other.x4 == x4 &&
|
||||
other.y1 == y1 &&
|
||||
other.y2 == y2 &&
|
||||
other.y3 == y3 &&
|
||||
other.y4 == y4;
|
||||
|
||||
@override
|
||||
int get hashCode =>
|
||||
// ignore: unnecessary_parenthesis
|
||||
(assetId.hashCode) +
|
||||
(boxScore.hashCode) +
|
||||
(id.hashCode) +
|
||||
(isVisible.hashCode) +
|
||||
(text.hashCode) +
|
||||
(textScore.hashCode) +
|
||||
(x1.hashCode) +
|
||||
(x2.hashCode) +
|
||||
(x3.hashCode) +
|
||||
(x4.hashCode) +
|
||||
(y1.hashCode) +
|
||||
(y2.hashCode) +
|
||||
(y3.hashCode) +
|
||||
(y4.hashCode);
|
||||
|
||||
@override
|
||||
String toString() => 'SyncAssetOcrV1[assetId=$assetId, boxScore=$boxScore, id=$id, isVisible=$isVisible, text=$text, textScore=$textScore, x1=$x1, x2=$x2, x3=$x3, x4=$x4, y1=$y1, y2=$y2, y3=$y3, y4=$y4]';
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final json = <String, dynamic>{};
|
||||
json[r'assetId'] = this.assetId;
|
||||
json[r'boxScore'] = this.boxScore;
|
||||
json[r'id'] = this.id;
|
||||
json[r'isVisible'] = this.isVisible;
|
||||
json[r'text'] = this.text;
|
||||
json[r'textScore'] = this.textScore;
|
||||
json[r'x1'] = this.x1;
|
||||
json[r'x2'] = this.x2;
|
||||
json[r'x3'] = this.x3;
|
||||
json[r'x4'] = this.x4;
|
||||
json[r'y1'] = this.y1;
|
||||
json[r'y2'] = this.y2;
|
||||
json[r'y3'] = this.y3;
|
||||
json[r'y4'] = this.y4;
|
||||
return json;
|
||||
}
|
||||
|
||||
/// Returns a new [SyncAssetOcrV1] instance and imports its values from
|
||||
/// [value] if it's a [Map], null otherwise.
|
||||
// ignore: prefer_constructors_over_static_methods
|
||||
static SyncAssetOcrV1? fromJson(dynamic value) {
|
||||
upgradeDto(value, "SyncAssetOcrV1");
|
||||
if (value is Map) {
|
||||
final json = value.cast<String, dynamic>();
|
||||
|
||||
return SyncAssetOcrV1(
|
||||
assetId: mapValueOfType<String>(json, r'assetId')!,
|
||||
boxScore: num.parse('${json[r'boxScore']}'),
|
||||
id: mapValueOfType<String>(json, r'id')!,
|
||||
isVisible: mapValueOfType<bool>(json, r'isVisible')!,
|
||||
text: mapValueOfType<String>(json, r'text')!,
|
||||
textScore: num.parse('${json[r'textScore']}'),
|
||||
x1: num.parse('${json[r'x1']}'),
|
||||
x2: num.parse('${json[r'x2']}'),
|
||||
x3: num.parse('${json[r'x3']}'),
|
||||
x4: num.parse('${json[r'x4']}'),
|
||||
y1: num.parse('${json[r'y1']}'),
|
||||
y2: num.parse('${json[r'y2']}'),
|
||||
y3: num.parse('${json[r'y3']}'),
|
||||
y4: num.parse('${json[r'y4']}'),
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static List<SyncAssetOcrV1> listFromJson(dynamic json, {bool growable = false,}) {
|
||||
final result = <SyncAssetOcrV1>[];
|
||||
if (json is List && json.isNotEmpty) {
|
||||
for (final row in json) {
|
||||
final value = SyncAssetOcrV1.fromJson(row);
|
||||
if (value != null) {
|
||||
result.add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toList(growable: growable);
|
||||
}
|
||||
|
||||
static Map<String, SyncAssetOcrV1> mapFromJson(dynamic json) {
|
||||
final map = <String, SyncAssetOcrV1>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||
for (final entry in json.entries) {
|
||||
final value = SyncAssetOcrV1.fromJson(entry.value);
|
||||
if (value != null) {
|
||||
map[entry.key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// maps a json object with a list of SyncAssetOcrV1-objects as value to a dart map
|
||||
static Map<String, List<SyncAssetOcrV1>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||
final map = <String, List<SyncAssetOcrV1>>{};
|
||||
if (json is Map && json.isNotEmpty) {
|
||||
// ignore: parameter_assignments
|
||||
json = json.cast<String, dynamic>();
|
||||
for (final entry in json.entries) {
|
||||
map[entry.key] = SyncAssetOcrV1.listFromJson(entry.value, growable: growable,);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/// The list of required keys that must be present in a JSON.
|
||||
static const requiredKeys = <String>{
|
||||
'assetId',
|
||||
'boxScore',
|
||||
'id',
|
||||
'isVisible',
|
||||
'text',
|
||||
'textScore',
|
||||
'x1',
|
||||
'x2',
|
||||
'x3',
|
||||
'x4',
|
||||
'y1',
|
||||
'y2',
|
||||
'y3',
|
||||
'y4',
|
||||
};
|
||||
}
|
||||
|
||||
3
mobile/openapi/lib/model/sync_entity_type.dart
generated
3
mobile/openapi/lib/model/sync_entity_type.dart
generated
@@ -31,6 +31,7 @@ class SyncEntityType {
|
||||
static const assetExifV1 = SyncEntityType._(r'AssetExifV1');
|
||||
static const assetMetadataV1 = SyncEntityType._(r'AssetMetadataV1');
|
||||
static const assetMetadataDeleteV1 = SyncEntityType._(r'AssetMetadataDeleteV1');
|
||||
static const assetOcrV1 = SyncEntityType._(r'AssetOcrV1');
|
||||
static const partnerV1 = SyncEntityType._(r'PartnerV1');
|
||||
static const partnerDeleteV1 = SyncEntityType._(r'PartnerDeleteV1');
|
||||
static const partnerAssetV1 = SyncEntityType._(r'PartnerAssetV1');
|
||||
@@ -82,6 +83,7 @@ class SyncEntityType {
|
||||
assetExifV1,
|
||||
assetMetadataV1,
|
||||
assetMetadataDeleteV1,
|
||||
assetOcrV1,
|
||||
partnerV1,
|
||||
partnerDeleteV1,
|
||||
partnerAssetV1,
|
||||
@@ -168,6 +170,7 @@ class SyncEntityTypeTypeTransformer {
|
||||
case r'AssetExifV1': return SyncEntityType.assetExifV1;
|
||||
case r'AssetMetadataV1': return SyncEntityType.assetMetadataV1;
|
||||
case r'AssetMetadataDeleteV1': return SyncEntityType.assetMetadataDeleteV1;
|
||||
case r'AssetOcrV1': return SyncEntityType.assetOcrV1;
|
||||
case r'PartnerV1': return SyncEntityType.partnerV1;
|
||||
case r'PartnerDeleteV1': return SyncEntityType.partnerDeleteV1;
|
||||
case r'PartnerAssetV1': return SyncEntityType.partnerAssetV1;
|
||||
|
||||
3
mobile/openapi/lib/model/sync_request_type.dart
generated
3
mobile/openapi/lib/model/sync_request_type.dart
generated
@@ -31,6 +31,7 @@ class SyncRequestType {
|
||||
static const assetsV1 = SyncRequestType._(r'AssetsV1');
|
||||
static const assetExifsV1 = SyncRequestType._(r'AssetExifsV1');
|
||||
static const assetMetadataV1 = SyncRequestType._(r'AssetMetadataV1');
|
||||
static const assetOcrV1 = SyncRequestType._(r'AssetOcrV1');
|
||||
static const authUsersV1 = SyncRequestType._(r'AuthUsersV1');
|
||||
static const memoriesV1 = SyncRequestType._(r'MemoriesV1');
|
||||
static const memoryToAssetsV1 = SyncRequestType._(r'MemoryToAssetsV1');
|
||||
@@ -55,6 +56,7 @@ class SyncRequestType {
|
||||
assetsV1,
|
||||
assetExifsV1,
|
||||
assetMetadataV1,
|
||||
assetOcrV1,
|
||||
authUsersV1,
|
||||
memoriesV1,
|
||||
memoryToAssetsV1,
|
||||
@@ -114,6 +116,7 @@ class SyncRequestTypeTypeTransformer {
|
||||
case r'AssetsV1': return SyncRequestType.assetsV1;
|
||||
case r'AssetExifsV1': return SyncRequestType.assetExifsV1;
|
||||
case r'AssetMetadataV1': return SyncRequestType.assetMetadataV1;
|
||||
case r'AssetOcrV1': return SyncRequestType.assetOcrV1;
|
||||
case r'AuthUsersV1': return SyncRequestType.authUsersV1;
|
||||
case r'MemoriesV1': return SyncRequestType.memoriesV1;
|
||||
case r'MemoryToAssetsV1': return SyncRequestType.memoryToAssetsV1;
|
||||
|
||||
@@ -22944,6 +22944,83 @@
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SyncAssetOcrV1": {
|
||||
"properties": {
|
||||
"assetId": {
|
||||
"description": "Asset ID",
|
||||
"type": "string"
|
||||
},
|
||||
"boxScore": {
|
||||
"description": "Confidence score of the bounding box",
|
||||
"type": "number"
|
||||
},
|
||||
"id": {
|
||||
"description": "OCR entry ID",
|
||||
"type": "string"
|
||||
},
|
||||
"isVisible": {
|
||||
"description": "Whether the OCR entry is visible",
|
||||
"type": "boolean"
|
||||
},
|
||||
"text": {
|
||||
"description": "Recognized text content",
|
||||
"type": "string"
|
||||
},
|
||||
"textScore": {
|
||||
"description": "Confidence score of the recognized text",
|
||||
"type": "number"
|
||||
},
|
||||
"x1": {
|
||||
"description": "Top-left X coordinate (normalized 0–1)",
|
||||
"type": "number"
|
||||
},
|
||||
"x2": {
|
||||
"description": "Top-right X coordinate (normalized 0–1)",
|
||||
"type": "number"
|
||||
},
|
||||
"x3": {
|
||||
"description": "Bottom-right X coordinate (normalized 0–1)",
|
||||
"type": "number"
|
||||
},
|
||||
"x4": {
|
||||
"description": "Bottom-left X coordinate (normalized 0–1)",
|
||||
"type": "number"
|
||||
},
|
||||
"y1": {
|
||||
"description": "Top-left Y coordinate (normalized 0–1)",
|
||||
"type": "number"
|
||||
},
|
||||
"y2": {
|
||||
"description": "Top-right Y coordinate (normalized 0–1)",
|
||||
"type": "number"
|
||||
},
|
||||
"y3": {
|
||||
"description": "Bottom-right Y coordinate (normalized 0–1)",
|
||||
"type": "number"
|
||||
},
|
||||
"y4": {
|
||||
"description": "Bottom-left Y coordinate (normalized 0–1)",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"assetId",
|
||||
"boxScore",
|
||||
"id",
|
||||
"isVisible",
|
||||
"text",
|
||||
"textScore",
|
||||
"x1",
|
||||
"x2",
|
||||
"x3",
|
||||
"x4",
|
||||
"y1",
|
||||
"y2",
|
||||
"y3",
|
||||
"y4"
|
||||
],
|
||||
"type": "object"
|
||||
},
|
||||
"SyncAssetV1": {
|
||||
"properties": {
|
||||
"checksum": {
|
||||
@@ -23165,6 +23242,7 @@
|
||||
"AssetExifV1",
|
||||
"AssetMetadataV1",
|
||||
"AssetMetadataDeleteV1",
|
||||
"AssetOcrV1",
|
||||
"PartnerV1",
|
||||
"PartnerDeleteV1",
|
||||
"PartnerAssetV1",
|
||||
@@ -23461,6 +23539,7 @@
|
||||
"AssetsV1",
|
||||
"AssetExifsV1",
|
||||
"AssetMetadataV1",
|
||||
"AssetOcrV1",
|
||||
"AuthUsersV1",
|
||||
"MemoriesV1",
|
||||
"MemoryToAssetsV1",
|
||||
|
||||
@@ -3073,6 +3073,36 @@ export type SyncAssetMetadataV1 = {
|
||||
/** Value */
|
||||
value: object;
|
||||
};
|
||||
export type SyncAssetOcrV1 = {
|
||||
/** Asset ID */
|
||||
assetId: string;
|
||||
/** Confidence score of the bounding box */
|
||||
boxScore: number;
|
||||
/** OCR entry ID */
|
||||
id: string;
|
||||
/** Whether the OCR entry is visible */
|
||||
isVisible: boolean;
|
||||
/** Recognized text content */
|
||||
text: string;
|
||||
/** Confidence score of the recognized text */
|
||||
textScore: number;
|
||||
/** Top-left X coordinate (normalized 0–1) */
|
||||
x1: number;
|
||||
/** Top-right X coordinate (normalized 0–1) */
|
||||
x2: number;
|
||||
/** Bottom-right X coordinate (normalized 0–1) */
|
||||
x3: number;
|
||||
/** Bottom-left X coordinate (normalized 0–1) */
|
||||
x4: number;
|
||||
/** Top-left Y coordinate (normalized 0–1) */
|
||||
y1: number;
|
||||
/** Top-right Y coordinate (normalized 0–1) */
|
||||
y2: number;
|
||||
/** Bottom-right Y coordinate (normalized 0–1) */
|
||||
y3: number;
|
||||
/** Bottom-left Y coordinate (normalized 0–1) */
|
||||
y4: number;
|
||||
};
|
||||
export type SyncAssetV1 = {
|
||||
/** Checksum */
|
||||
checksum: string;
|
||||
@@ -7232,6 +7262,7 @@ export enum SyncEntityType {
|
||||
AssetExifV1 = "AssetExifV1",
|
||||
AssetMetadataV1 = "AssetMetadataV1",
|
||||
AssetMetadataDeleteV1 = "AssetMetadataDeleteV1",
|
||||
AssetOcrV1 = "AssetOcrV1",
|
||||
PartnerV1 = "PartnerV1",
|
||||
PartnerDeleteV1 = "PartnerDeleteV1",
|
||||
PartnerAssetV1 = "PartnerAssetV1",
|
||||
@@ -7282,6 +7313,7 @@ export enum SyncRequestType {
|
||||
AssetsV1 = "AssetsV1",
|
||||
AssetExifsV1 = "AssetExifsV1",
|
||||
AssetMetadataV1 = "AssetMetadataV1",
|
||||
AssetOcrV1 = "AssetOcrV1",
|
||||
AuthUsersV1 = "AuthUsersV1",
|
||||
MemoriesV1 = "MemoriesV1",
|
||||
MemoryToAssetsV1 = "MemoryToAssetsV1",
|
||||
|
||||
@@ -437,6 +437,23 @@ export const columns = {
|
||||
'asset_exif.rating',
|
||||
'asset_exif.fps',
|
||||
],
|
||||
syncAssetOcr: [
|
||||
'id',
|
||||
'assetId',
|
||||
'x1',
|
||||
'y1',
|
||||
'x2',
|
||||
'y2',
|
||||
'x3',
|
||||
'y3',
|
||||
'x4',
|
||||
'y4',
|
||||
'text',
|
||||
'boxScore',
|
||||
'textScore',
|
||||
'updateId',
|
||||
'isVisible',
|
||||
],
|
||||
exif: [
|
||||
'asset_exif.assetId',
|
||||
'asset_exif.autoStackId',
|
||||
|
||||
@@ -218,6 +218,51 @@ export class SyncAssetExifV1 {
|
||||
fps!: number | null;
|
||||
}
|
||||
|
||||
@ExtraModel()
|
||||
export class SyncAssetOcrV1 {
|
||||
@ApiProperty({ description: 'OCR entry ID' })
|
||||
id!: string;
|
||||
|
||||
@ApiProperty({ description: 'Asset ID' })
|
||||
assetId!: string;
|
||||
|
||||
@ApiProperty({ description: 'Top-left X coordinate (normalized 0–1)' })
|
||||
x1!: number;
|
||||
|
||||
@ApiProperty({ description: 'Top-left Y coordinate (normalized 0–1)' })
|
||||
y1!: number;
|
||||
|
||||
@ApiProperty({ description: 'Top-right X coordinate (normalized 0–1)' })
|
||||
x2!: number;
|
||||
|
||||
@ApiProperty({ description: 'Top-right Y coordinate (normalized 0–1)' })
|
||||
y2!: number;
|
||||
|
||||
@ApiProperty({ description: 'Bottom-right X coordinate (normalized 0–1)' })
|
||||
x3!: number;
|
||||
|
||||
@ApiProperty({ description: 'Bottom-right Y coordinate (normalized 0–1)' })
|
||||
y3!: number;
|
||||
|
||||
@ApiProperty({ description: 'Bottom-left X coordinate (normalized 0–1)' })
|
||||
x4!: number;
|
||||
|
||||
@ApiProperty({ description: 'Bottom-left Y coordinate (normalized 0–1)' })
|
||||
y4!: number;
|
||||
|
||||
@ApiProperty({ description: 'Confidence score of the bounding box' })
|
||||
boxScore!: number;
|
||||
|
||||
@ApiProperty({ description: 'Confidence score of the recognized text' })
|
||||
textScore!: number;
|
||||
|
||||
@ApiProperty({ description: 'Recognized text content' })
|
||||
text!: string;
|
||||
|
||||
@ApiProperty({ description: 'Whether the OCR entry is visible' })
|
||||
isVisible!: boolean;
|
||||
}
|
||||
|
||||
@ExtraModel()
|
||||
export class SyncAssetMetadataV1 {
|
||||
@ApiProperty({ description: 'Asset ID' })
|
||||
@@ -480,6 +525,7 @@ export type SyncItem = {
|
||||
[SyncEntityType.AssetMetadataV1]: SyncAssetMetadataV1;
|
||||
[SyncEntityType.AssetMetadataDeleteV1]: SyncAssetMetadataDeleteV1;
|
||||
[SyncEntityType.AssetExifV1]: SyncAssetExifV1;
|
||||
[SyncEntityType.AssetOcrV1]: SyncAssetOcrV1;
|
||||
[SyncEntityType.PartnerAssetV1]: SyncAssetV1;
|
||||
[SyncEntityType.PartnerAssetBackfillV1]: SyncAssetV1;
|
||||
[SyncEntityType.PartnerAssetDeleteV1]: SyncAssetDeleteV1;
|
||||
|
||||
@@ -721,6 +721,7 @@ export enum SyncRequestType {
|
||||
AssetsV1 = 'AssetsV1',
|
||||
AssetExifsV1 = 'AssetExifsV1',
|
||||
AssetMetadataV1 = 'AssetMetadataV1',
|
||||
AssetOcrV1 = 'AssetOcrV1',
|
||||
AuthUsersV1 = 'AuthUsersV1',
|
||||
MemoriesV1 = 'MemoriesV1',
|
||||
MemoryToAssetsV1 = 'MemoryToAssetsV1',
|
||||
@@ -747,6 +748,7 @@ export enum SyncEntityType {
|
||||
AssetExifV1 = 'AssetExifV1',
|
||||
AssetMetadataV1 = 'AssetMetadataV1',
|
||||
AssetMetadataDeleteV1 = 'AssetMetadataDeleteV1',
|
||||
AssetOcrV1 = 'AssetOcrV1',
|
||||
|
||||
PartnerV1 = 'PartnerV1',
|
||||
PartnerDeleteV1 = 'PartnerDeleteV1',
|
||||
|
||||
@@ -55,6 +55,7 @@ export class SyncRepository {
|
||||
assetExif: AssetExifSync;
|
||||
assetFace: AssetFaceSync;
|
||||
assetMetadata: AssetMetadataSync;
|
||||
assetOcr: AssetOcrSync;
|
||||
authUser: AuthUserSync;
|
||||
memory: MemorySync;
|
||||
memoryToAsset: MemoryToAssetSync;
|
||||
@@ -77,6 +78,7 @@ export class SyncRepository {
|
||||
this.assetExif = new AssetExifSync(this.db);
|
||||
this.assetFace = new AssetFaceSync(this.db);
|
||||
this.assetMetadata = new AssetMetadataSync(this.db);
|
||||
this.assetOcr = new AssetOcrSync(this.db);
|
||||
this.authUser = new AuthUserSync(this.db);
|
||||
this.memory = new MemorySync(this.db);
|
||||
this.memoryToAsset = new MemoryToAssetSync(this.db);
|
||||
@@ -772,3 +774,14 @@ class AssetMetadataSync extends BaseSync {
|
||||
.stream();
|
||||
}
|
||||
}
|
||||
|
||||
class AssetOcrSync extends BaseSync {
|
||||
@GenerateSql({ params: [dummyQueryOptions, DummyValue.UUID], stream: true })
|
||||
getUpserts(options: SyncQueryOptions, userId: string) {
|
||||
return this.upsertQuery('asset_ocr', options)
|
||||
.select(columns.syncAssetOcr)
|
||||
.innerJoin('asset', 'asset.id', 'asset_ocr.assetId')
|
||||
.where('asset.ownerId', '=', userId)
|
||||
.stream();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Kysely, sql } from 'kysely';
|
||||
|
||||
export async function up(db: Kysely<any>): Promise<void> {
|
||||
await sql`ALTER TABLE "asset_ocr" ADD "updateId" uuid NOT NULL DEFAULT immich_uuid_v7();`.execute(db);
|
||||
await sql`CREATE INDEX "asset_ocr_updateId_idx" ON "asset_ocr" ("updateId");`.execute(db);
|
||||
}
|
||||
|
||||
export async function down(db: Kysely<any>): Promise<void> {
|
||||
await sql`DROP INDEX "asset_ocr_updateId_idx";`.execute(db);
|
||||
await sql`ALTER TABLE "asset_ocr" DROP COLUMN "updateId";`.execute(db);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Column, ForeignKeyColumn, Generated, PrimaryGeneratedColumn, Table } from '@immich/sql-tools';
|
||||
import { UpdateIdColumn } from 'src/decorators';
|
||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||
|
||||
@Table('asset_ocr')
|
||||
@@ -45,4 +46,7 @@ export class AssetOcrTable {
|
||||
|
||||
@Column({ type: 'boolean', default: true })
|
||||
isVisible!: Generated<boolean>;
|
||||
|
||||
@UpdateIdColumn({ index: true })
|
||||
updateId!: Generated<string>;
|
||||
}
|
||||
|
||||
@@ -193,6 +193,7 @@ export class SyncService extends BaseService {
|
||||
[SyncRequestType.AssetFacesV1]: async () => this.syncAssetFacesV1(options, response, checkpointMap),
|
||||
[SyncRequestType.AssetFacesV2]: async () => this.syncAssetFacesV2(options, response, checkpointMap),
|
||||
[SyncRequestType.UserMetadataV1]: () => this.syncUserMetadataV1(options, response, checkpointMap),
|
||||
[SyncRequestType.AssetOcrV1]: () => this.syncAssetOcrV1(options, response, checkpointMap, auth),
|
||||
};
|
||||
|
||||
for (const type of SYNC_TYPES_ORDER.filter((type) => dto.types.includes(type))) {
|
||||
@@ -855,6 +856,23 @@ export class SyncService extends BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
private async syncAssetOcrV1(
|
||||
options: SyncQueryOptions,
|
||||
response: Writable,
|
||||
checkpointMap: CheckpointMap,
|
||||
auth: AuthDto,
|
||||
) {
|
||||
const upsertType = SyncEntityType.AssetOcrV1;
|
||||
const upserts = this.syncRepository.assetOcr.getUpserts(
|
||||
{ ...options, ack: checkpointMap[upsertType] },
|
||||
auth.user.id,
|
||||
);
|
||||
|
||||
for await (const { updateId, ...data } of upserts) {
|
||||
send(response, { type: upsertType, ids: [updateId], data });
|
||||
}
|
||||
}
|
||||
|
||||
private async upsertBackfillCheckpoint(item: { type: SyncEntityType; sessionId: string; createId: string }) {
|
||||
const { type, sessionId, createId } = item;
|
||||
await this.syncCheckpointRepository.upsertAll([
|
||||
|
||||
Reference in New Issue
Block a user