mirror of
https://github.com/immich-app/immich.git
synced 2025-05-24 02:13:51 -04:00
feat(server): add originalFileName to asset table (#2231)
This commit is contained in:
parent
db628cec11
commit
a1a62b00a0
1
mobile/openapi/doc/AssetResponseDto.md
generated
1
mobile/openapi/doc/AssetResponseDto.md
generated
@ -14,6 +14,7 @@ Name | Type | Description | Notes
|
|||||||
**ownerId** | **String** | |
|
**ownerId** | **String** | |
|
||||||
**deviceId** | **String** | |
|
**deviceId** | **String** | |
|
||||||
**originalPath** | **String** | |
|
**originalPath** | **String** | |
|
||||||
|
**originalFileName** | **String** | |
|
||||||
**resizePath** | **String** | |
|
**resizePath** | **String** | |
|
||||||
**fileCreatedAt** | **String** | |
|
**fileCreatedAt** | **String** | |
|
||||||
**fileModifiedAt** | **String** | |
|
**fileModifiedAt** | **String** | |
|
||||||
|
1
mobile/openapi/doc/ExifResponseDto.md
generated
1
mobile/openapi/doc/ExifResponseDto.md
generated
@ -11,7 +11,6 @@ Name | Type | Description | Notes
|
|||||||
**fileSizeInByte** | **int** | | [optional]
|
**fileSizeInByte** | **int** | | [optional]
|
||||||
**make** | **String** | | [optional]
|
**make** | **String** | | [optional]
|
||||||
**model** | **String** | | [optional]
|
**model** | **String** | | [optional]
|
||||||
**imageName** | **String** | | [optional]
|
|
||||||
**exifImageWidth** | **num** | | [optional]
|
**exifImageWidth** | **num** | | [optional]
|
||||||
**exifImageHeight** | **num** | | [optional]
|
**exifImageHeight** | **num** | | [optional]
|
||||||
**orientation** | **String** | | [optional]
|
**orientation** | **String** | | [optional]
|
||||||
|
10
mobile/openapi/lib/model/asset_response_dto.dart
generated
10
mobile/openapi/lib/model/asset_response_dto.dart
generated
@ -19,6 +19,7 @@ class AssetResponseDto {
|
|||||||
required this.ownerId,
|
required this.ownerId,
|
||||||
required this.deviceId,
|
required this.deviceId,
|
||||||
required this.originalPath,
|
required this.originalPath,
|
||||||
|
required this.originalFileName,
|
||||||
required this.resizePath,
|
required this.resizePath,
|
||||||
required this.fileCreatedAt,
|
required this.fileCreatedAt,
|
||||||
required this.fileModifiedAt,
|
required this.fileModifiedAt,
|
||||||
@ -46,6 +47,8 @@ class AssetResponseDto {
|
|||||||
|
|
||||||
String originalPath;
|
String originalPath;
|
||||||
|
|
||||||
|
String originalFileName;
|
||||||
|
|
||||||
String? resizePath;
|
String? resizePath;
|
||||||
|
|
||||||
String fileCreatedAt;
|
String fileCreatedAt;
|
||||||
@ -92,6 +95,7 @@ class AssetResponseDto {
|
|||||||
other.ownerId == ownerId &&
|
other.ownerId == ownerId &&
|
||||||
other.deviceId == deviceId &&
|
other.deviceId == deviceId &&
|
||||||
other.originalPath == originalPath &&
|
other.originalPath == originalPath &&
|
||||||
|
other.originalFileName == originalFileName &&
|
||||||
other.resizePath == resizePath &&
|
other.resizePath == resizePath &&
|
||||||
other.fileCreatedAt == fileCreatedAt &&
|
other.fileCreatedAt == fileCreatedAt &&
|
||||||
other.fileModifiedAt == fileModifiedAt &&
|
other.fileModifiedAt == fileModifiedAt &&
|
||||||
@ -115,6 +119,7 @@ class AssetResponseDto {
|
|||||||
(ownerId.hashCode) +
|
(ownerId.hashCode) +
|
||||||
(deviceId.hashCode) +
|
(deviceId.hashCode) +
|
||||||
(originalPath.hashCode) +
|
(originalPath.hashCode) +
|
||||||
|
(originalFileName.hashCode) +
|
||||||
(resizePath == null ? 0 : resizePath!.hashCode) +
|
(resizePath == null ? 0 : resizePath!.hashCode) +
|
||||||
(fileCreatedAt.hashCode) +
|
(fileCreatedAt.hashCode) +
|
||||||
(fileModifiedAt.hashCode) +
|
(fileModifiedAt.hashCode) +
|
||||||
@ -130,7 +135,7 @@ class AssetResponseDto {
|
|||||||
(tags.hashCode);
|
(tags.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, resizePath=$resizePath, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, updatedAt=$updatedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo, livePhotoVideoId=$livePhotoVideoId, tags=$tags]';
|
String toString() => 'AssetResponseDto[type=$type, id=$id, deviceAssetId=$deviceAssetId, ownerId=$ownerId, deviceId=$deviceId, originalPath=$originalPath, originalFileName=$originalFileName, resizePath=$resizePath, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, updatedAt=$updatedAt, isFavorite=$isFavorite, mimeType=$mimeType, duration=$duration, webpPath=$webpPath, encodedVideoPath=$encodedVideoPath, exifInfo=$exifInfo, smartInfo=$smartInfo, livePhotoVideoId=$livePhotoVideoId, tags=$tags]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
@ -140,6 +145,7 @@ class AssetResponseDto {
|
|||||||
json[r'ownerId'] = this.ownerId;
|
json[r'ownerId'] = this.ownerId;
|
||||||
json[r'deviceId'] = this.deviceId;
|
json[r'deviceId'] = this.deviceId;
|
||||||
json[r'originalPath'] = this.originalPath;
|
json[r'originalPath'] = this.originalPath;
|
||||||
|
json[r'originalFileName'] = this.originalFileName;
|
||||||
if (this.resizePath != null) {
|
if (this.resizePath != null) {
|
||||||
json[r'resizePath'] = this.resizePath;
|
json[r'resizePath'] = this.resizePath;
|
||||||
} else {
|
} else {
|
||||||
@ -209,6 +215,7 @@ class AssetResponseDto {
|
|||||||
ownerId: mapValueOfType<String>(json, r'ownerId')!,
|
ownerId: mapValueOfType<String>(json, r'ownerId')!,
|
||||||
deviceId: mapValueOfType<String>(json, r'deviceId')!,
|
deviceId: mapValueOfType<String>(json, r'deviceId')!,
|
||||||
originalPath: mapValueOfType<String>(json, r'originalPath')!,
|
originalPath: mapValueOfType<String>(json, r'originalPath')!,
|
||||||
|
originalFileName: mapValueOfType<String>(json, r'originalFileName')!,
|
||||||
resizePath: mapValueOfType<String>(json, r'resizePath'),
|
resizePath: mapValueOfType<String>(json, r'resizePath'),
|
||||||
fileCreatedAt: mapValueOfType<String>(json, r'fileCreatedAt')!,
|
fileCreatedAt: mapValueOfType<String>(json, r'fileCreatedAt')!,
|
||||||
fileModifiedAt: mapValueOfType<String>(json, r'fileModifiedAt')!,
|
fileModifiedAt: mapValueOfType<String>(json, r'fileModifiedAt')!,
|
||||||
@ -277,6 +284,7 @@ class AssetResponseDto {
|
|||||||
'ownerId',
|
'ownerId',
|
||||||
'deviceId',
|
'deviceId',
|
||||||
'originalPath',
|
'originalPath',
|
||||||
|
'originalFileName',
|
||||||
'resizePath',
|
'resizePath',
|
||||||
'fileCreatedAt',
|
'fileCreatedAt',
|
||||||
'fileModifiedAt',
|
'fileModifiedAt',
|
||||||
|
13
mobile/openapi/lib/model/exif_response_dto.dart
generated
13
mobile/openapi/lib/model/exif_response_dto.dart
generated
@ -16,7 +16,6 @@ class ExifResponseDto {
|
|||||||
this.fileSizeInByte,
|
this.fileSizeInByte,
|
||||||
this.make,
|
this.make,
|
||||||
this.model,
|
this.model,
|
||||||
this.imageName,
|
|
||||||
this.exifImageWidth,
|
this.exifImageWidth,
|
||||||
this.exifImageHeight,
|
this.exifImageHeight,
|
||||||
this.orientation,
|
this.orientation,
|
||||||
@ -41,8 +40,6 @@ class ExifResponseDto {
|
|||||||
|
|
||||||
String? model;
|
String? model;
|
||||||
|
|
||||||
String? imageName;
|
|
||||||
|
|
||||||
num? exifImageWidth;
|
num? exifImageWidth;
|
||||||
|
|
||||||
num? exifImageHeight;
|
num? exifImageHeight;
|
||||||
@ -80,7 +77,6 @@ class ExifResponseDto {
|
|||||||
other.fileSizeInByte == fileSizeInByte &&
|
other.fileSizeInByte == fileSizeInByte &&
|
||||||
other.make == make &&
|
other.make == make &&
|
||||||
other.model == model &&
|
other.model == model &&
|
||||||
other.imageName == imageName &&
|
|
||||||
other.exifImageWidth == exifImageWidth &&
|
other.exifImageWidth == exifImageWidth &&
|
||||||
other.exifImageHeight == exifImageHeight &&
|
other.exifImageHeight == exifImageHeight &&
|
||||||
other.orientation == orientation &&
|
other.orientation == orientation &&
|
||||||
@ -104,7 +100,6 @@ class ExifResponseDto {
|
|||||||
(fileSizeInByte == null ? 0 : fileSizeInByte!.hashCode) +
|
(fileSizeInByte == null ? 0 : fileSizeInByte!.hashCode) +
|
||||||
(make == null ? 0 : make!.hashCode) +
|
(make == null ? 0 : make!.hashCode) +
|
||||||
(model == null ? 0 : model!.hashCode) +
|
(model == null ? 0 : model!.hashCode) +
|
||||||
(imageName == null ? 0 : imageName!.hashCode) +
|
|
||||||
(exifImageWidth == null ? 0 : exifImageWidth!.hashCode) +
|
(exifImageWidth == null ? 0 : exifImageWidth!.hashCode) +
|
||||||
(exifImageHeight == null ? 0 : exifImageHeight!.hashCode) +
|
(exifImageHeight == null ? 0 : exifImageHeight!.hashCode) +
|
||||||
(orientation == null ? 0 : orientation!.hashCode) +
|
(orientation == null ? 0 : orientation!.hashCode) +
|
||||||
@ -123,7 +118,7 @@ class ExifResponseDto {
|
|||||||
(country == null ? 0 : country!.hashCode);
|
(country == null ? 0 : country!.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'ExifResponseDto[fileSizeInByte=$fileSizeInByte, make=$make, model=$model, imageName=$imageName, exifImageWidth=$exifImageWidth, exifImageHeight=$exifImageHeight, orientation=$orientation, dateTimeOriginal=$dateTimeOriginal, modifyDate=$modifyDate, timeZone=$timeZone, lensModel=$lensModel, fNumber=$fNumber, focalLength=$focalLength, iso=$iso, exposureTime=$exposureTime, latitude=$latitude, longitude=$longitude, city=$city, state=$state, country=$country]';
|
String toString() => 'ExifResponseDto[fileSizeInByte=$fileSizeInByte, make=$make, model=$model, exifImageWidth=$exifImageWidth, exifImageHeight=$exifImageHeight, orientation=$orientation, dateTimeOriginal=$dateTimeOriginal, modifyDate=$modifyDate, timeZone=$timeZone, lensModel=$lensModel, fNumber=$fNumber, focalLength=$focalLength, iso=$iso, exposureTime=$exposureTime, latitude=$latitude, longitude=$longitude, city=$city, state=$state, country=$country]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
@ -142,11 +137,6 @@ class ExifResponseDto {
|
|||||||
} else {
|
} else {
|
||||||
// json[r'model'] = null;
|
// json[r'model'] = null;
|
||||||
}
|
}
|
||||||
if (this.imageName != null) {
|
|
||||||
json[r'imageName'] = this.imageName;
|
|
||||||
} else {
|
|
||||||
// json[r'imageName'] = null;
|
|
||||||
}
|
|
||||||
if (this.exifImageWidth != null) {
|
if (this.exifImageWidth != null) {
|
||||||
json[r'exifImageWidth'] = this.exifImageWidth;
|
json[r'exifImageWidth'] = this.exifImageWidth;
|
||||||
} else {
|
} else {
|
||||||
@ -252,7 +242,6 @@ class ExifResponseDto {
|
|||||||
fileSizeInByte: mapValueOfType<int>(json, r'fileSizeInByte'),
|
fileSizeInByte: mapValueOfType<int>(json, r'fileSizeInByte'),
|
||||||
make: mapValueOfType<String>(json, r'make'),
|
make: mapValueOfType<String>(json, r'make'),
|
||||||
model: mapValueOfType<String>(json, r'model'),
|
model: mapValueOfType<String>(json, r'model'),
|
||||||
imageName: mapValueOfType<String>(json, r'imageName'),
|
|
||||||
exifImageWidth: json[r'exifImageWidth'] == null
|
exifImageWidth: json[r'exifImageWidth'] == null
|
||||||
? null
|
? null
|
||||||
: num.parse(json[r'exifImageWidth'].toString()),
|
: num.parse(json[r'exifImageWidth'].toString()),
|
||||||
|
5
mobile/openapi/test/asset_response_dto_test.dart
generated
5
mobile/openapi/test/asset_response_dto_test.dart
generated
@ -46,6 +46,11 @@ void main() {
|
|||||||
// TODO
|
// TODO
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// String originalFileName
|
||||||
|
test('to test the property `originalFileName`', () async {
|
||||||
|
// TODO
|
||||||
|
});
|
||||||
|
|
||||||
// String resizePath
|
// String resizePath
|
||||||
test('to test the property `resizePath`', () async {
|
test('to test the property `resizePath`', () async {
|
||||||
// TODO
|
// TODO
|
||||||
|
5
mobile/openapi/test/exif_response_dto_test.dart
generated
5
mobile/openapi/test/exif_response_dto_test.dart
generated
@ -31,11 +31,6 @@ void main() {
|
|||||||
// TODO
|
// TODO
|
||||||
});
|
});
|
||||||
|
|
||||||
// String imageName
|
|
||||||
test('to test the property `imageName`', () async {
|
|
||||||
// TODO
|
|
||||||
});
|
|
||||||
|
|
||||||
// num exifImageWidth
|
// num exifImageWidth
|
||||||
test('to test the property `exifImageWidth`', () async {
|
test('to test the property `exifImageWidth`', () async {
|
||||||
// TODO
|
// TODO
|
||||||
|
@ -2,6 +2,7 @@ import { AuthUserDto, IJobRepository, JobName } from '@app/domain';
|
|||||||
import { AssetEntity, UserEntity } from '@app/infra/entities';
|
import { AssetEntity, UserEntity } from '@app/infra/entities';
|
||||||
import { IAssetRepository } from './asset-repository';
|
import { IAssetRepository } from './asset-repository';
|
||||||
import { CreateAssetDto, UploadFile } from './dto/create-asset.dto';
|
import { CreateAssetDto, UploadFile } from './dto/create-asset.dto';
|
||||||
|
import { parse } from 'node:path';
|
||||||
|
|
||||||
export class AssetCore {
|
export class AssetCore {
|
||||||
constructor(private repository: IAssetRepository, private jobRepository: IJobRepository) {}
|
constructor(private repository: IAssetRepository, private jobRepository: IJobRepository) {}
|
||||||
@ -35,6 +36,7 @@ export class AssetCore {
|
|||||||
encodedVideoPath: null,
|
encodedVideoPath: null,
|
||||||
tags: [],
|
tags: [],
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
|
originalFileName: parse(file.originalName).name,
|
||||||
});
|
});
|
||||||
|
|
||||||
await this.jobRepository.queue({ name: JobName.ASSET_UPLOADED, data: { asset, fileName: file.originalName } });
|
await this.jobRepository.queue({ name: JobName.ASSET_UPLOADED, data: { asset, fileName: file.originalName } });
|
||||||
|
@ -10,9 +10,6 @@ export class CreateExifDto {
|
|||||||
@IsOptional()
|
@IsOptional()
|
||||||
model?: string;
|
model?: string;
|
||||||
|
|
||||||
@IsOptional()
|
|
||||||
imageName?: string;
|
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
exifImageWidth?: number;
|
exifImageWidth?: number;
|
||||||
|
|
||||||
|
@ -28,8 +28,8 @@ export class DownloadService {
|
|||||||
let fileCount = 0;
|
let fileCount = 0;
|
||||||
let complete = true;
|
let complete = true;
|
||||||
|
|
||||||
for (const { id, originalPath, exifInfo } of assets) {
|
for (const { originalPath, exifInfo, originalFileName } of assets) {
|
||||||
const name = `${exifInfo?.imageName || id}${extname(originalPath)}`;
|
const name = `${originalFileName}${extname(originalPath)}`;
|
||||||
archive.file(originalPath, { name });
|
archive.file(originalPath, { name });
|
||||||
totalSize += Number(exifInfo?.fileSizeInByte || 0);
|
totalSize += Number(exifInfo?.fileSizeInByte || 0);
|
||||||
fileCount++;
|
fileCount++;
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
AssetCore,
|
AssetCore,
|
||||||
getFileNameWithoutExtension,
|
|
||||||
IAssetRepository,
|
IAssetRepository,
|
||||||
IAssetUploadedJob,
|
IAssetUploadedJob,
|
||||||
IBaseJob,
|
IBaseJob,
|
||||||
@ -21,7 +20,6 @@ import { ExifDateTime, exiftool, Tags } from 'exiftool-vendored';
|
|||||||
import ffmpeg, { FfprobeData } from 'fluent-ffmpeg';
|
import ffmpeg, { FfprobeData } from 'fluent-ffmpeg';
|
||||||
import { Duration } from 'luxon';
|
import { Duration } from 'luxon';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import path from 'path';
|
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import { Repository } from 'typeorm/repository/Repository';
|
import { Repository } from 'typeorm/repository/Repository';
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
@ -79,7 +77,7 @@ export class MetadataExtractionProcessor {
|
|||||||
: await this.assetRepository.getWithout(WithoutProperty.EXIF);
|
: await this.assetRepository.getWithout(WithoutProperty.EXIF);
|
||||||
|
|
||||||
for (const asset of assets) {
|
for (const asset of assets) {
|
||||||
const fileName = asset.exifInfo?.imageName ?? getFileNameWithoutExtension(asset.originalPath);
|
const fileName = asset.originalFileName;
|
||||||
const name = asset.type === AssetType.VIDEO ? JobName.EXTRACT_VIDEO_METADATA : JobName.EXIF_EXTRACTION;
|
const name = asset.type === AssetType.VIDEO ? JobName.EXTRACT_VIDEO_METADATA : JobName.EXIF_EXTRACTION;
|
||||||
await this.jobRepository.queue({ name, data: { asset, fileName } });
|
await this.jobRepository.queue({ name, data: { asset, fileName } });
|
||||||
}
|
}
|
||||||
@ -92,7 +90,6 @@ export class MetadataExtractionProcessor {
|
|||||||
async extractExifInfo(job: Job<IAssetUploadedJob>) {
|
async extractExifInfo(job: Job<IAssetUploadedJob>) {
|
||||||
try {
|
try {
|
||||||
let asset = job.data.asset;
|
let asset = job.data.asset;
|
||||||
const fileName = job.data.fileName;
|
|
||||||
const exifData = await exiftool.read<ImmichTags>(asset.originalPath).catch((e) => {
|
const exifData = await exiftool.read<ImmichTags>(asset.originalPath).catch((e) => {
|
||||||
this.logger.warn(`The exifData parsing failed due to: ${e} on file ${asset.originalPath}`);
|
this.logger.warn(`The exifData parsing failed due to: ${e} on file ${asset.originalPath}`);
|
||||||
return null;
|
return null;
|
||||||
@ -126,7 +123,6 @@ export class MetadataExtractionProcessor {
|
|||||||
|
|
||||||
const newExif = new ExifEntity();
|
const newExif = new ExifEntity();
|
||||||
newExif.assetId = asset.id;
|
newExif.assetId = asset.id;
|
||||||
newExif.imageName = path.parse(fileName).name;
|
|
||||||
newExif.fileSizeInByte = fileSizeInBytes;
|
newExif.fileSizeInByte = fileSizeInBytes;
|
||||||
newExif.make = exifData?.Make || null;
|
newExif.make = exifData?.Make || null;
|
||||||
newExif.model = exifData?.Model || null;
|
newExif.model = exifData?.Model || null;
|
||||||
@ -191,7 +187,6 @@ export class MetadataExtractionProcessor {
|
|||||||
@Process({ name: JobName.EXTRACT_VIDEO_METADATA, concurrency: 2 })
|
@Process({ name: JobName.EXTRACT_VIDEO_METADATA, concurrency: 2 })
|
||||||
async extractVideoMetadata(job: Job<IAssetUploadedJob>) {
|
async extractVideoMetadata(job: Job<IAssetUploadedJob>) {
|
||||||
let asset = job.data.asset;
|
let asset = job.data.asset;
|
||||||
const fileName = job.data.fileName;
|
|
||||||
|
|
||||||
if (!asset.isVisible) {
|
if (!asset.isVisible) {
|
||||||
return;
|
return;
|
||||||
@ -219,7 +214,6 @@ export class MetadataExtractionProcessor {
|
|||||||
const newExif = new ExifEntity();
|
const newExif = new ExifEntity();
|
||||||
newExif.assetId = asset.id;
|
newExif.assetId = asset.id;
|
||||||
newExif.description = '';
|
newExif.description = '';
|
||||||
newExif.imageName = path.parse(fileName).name || null;
|
|
||||||
newExif.fileSizeInByte = data.format.size || null;
|
newExif.fileSizeInByte = data.format.size || null;
|
||||||
newExif.dateTimeOriginal = fileCreatedAt ? new Date(fileCreatedAt) : null;
|
newExif.dateTimeOriginal = fileCreatedAt ? new Date(fileCreatedAt) : null;
|
||||||
newExif.modifyDate = null;
|
newExif.modifyDate = null;
|
||||||
@ -242,7 +236,6 @@ export class MetadataExtractionProcessor {
|
|||||||
if (photoAsset) {
|
if (photoAsset) {
|
||||||
await this.assetCore.save({ id: photoAsset.id, livePhotoVideoId: asset.id });
|
await this.assetCore.save({ id: photoAsset.id, livePhotoVideoId: asset.id });
|
||||||
await this.assetCore.save({ id: asset.id, isVisible: false });
|
await this.assetCore.save({ id: asset.id, isVisible: false });
|
||||||
newExif.imageName = (photoAsset.exifInfo as ExifEntity).imageName;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3548,11 +3548,6 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"default": null
|
"default": null
|
||||||
},
|
},
|
||||||
"imageName": {
|
|
||||||
"type": "string",
|
|
||||||
"nullable": true,
|
|
||||||
"default": null
|
|
||||||
},
|
|
||||||
"exifImageWidth": {
|
"exifImageWidth": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
"nullable": true,
|
"nullable": true,
|
||||||
@ -3712,6 +3707,9 @@
|
|||||||
"originalPath": {
|
"originalPath": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"originalFileName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"resizePath": {
|
"resizePath": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"nullable": true
|
"nullable": true
|
||||||
@ -3767,6 +3765,7 @@
|
|||||||
"ownerId",
|
"ownerId",
|
||||||
"deviceId",
|
"deviceId",
|
||||||
"originalPath",
|
"originalPath",
|
||||||
|
"originalFileName",
|
||||||
"resizePath",
|
"resizePath",
|
||||||
"fileCreatedAt",
|
"fileCreatedAt",
|
||||||
"fileModifiedAt",
|
"fileModifiedAt",
|
||||||
|
@ -13,6 +13,7 @@ export class AssetResponseDto {
|
|||||||
@ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
|
@ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType })
|
||||||
type!: AssetType;
|
type!: AssetType;
|
||||||
originalPath!: string;
|
originalPath!: string;
|
||||||
|
originalFileName!: string;
|
||||||
resizePath!: string | null;
|
resizePath!: string | null;
|
||||||
fileCreatedAt!: string;
|
fileCreatedAt!: string;
|
||||||
fileModifiedAt!: string;
|
fileModifiedAt!: string;
|
||||||
@ -36,6 +37,7 @@ export function mapAsset(entity: AssetEntity): AssetResponseDto {
|
|||||||
deviceId: entity.deviceId,
|
deviceId: entity.deviceId,
|
||||||
type: entity.type,
|
type: entity.type,
|
||||||
originalPath: entity.originalPath,
|
originalPath: entity.originalPath,
|
||||||
|
originalFileName: entity.originalFileName,
|
||||||
resizePath: entity.resizePath,
|
resizePath: entity.resizePath,
|
||||||
fileCreatedAt: entity.fileCreatedAt,
|
fileCreatedAt: entity.fileCreatedAt,
|
||||||
fileModifiedAt: entity.fileModifiedAt,
|
fileModifiedAt: entity.fileModifiedAt,
|
||||||
@ -60,6 +62,7 @@ export function mapAssetWithoutExif(entity: AssetEntity): AssetResponseDto {
|
|||||||
deviceId: entity.deviceId,
|
deviceId: entity.deviceId,
|
||||||
type: entity.type,
|
type: entity.type,
|
||||||
originalPath: entity.originalPath,
|
originalPath: entity.originalPath,
|
||||||
|
originalFileName: entity.originalFileName,
|
||||||
resizePath: entity.resizePath,
|
resizePath: entity.resizePath,
|
||||||
fileCreatedAt: entity.fileCreatedAt,
|
fileCreatedAt: entity.fileCreatedAt,
|
||||||
fileModifiedAt: entity.fileModifiedAt,
|
fileModifiedAt: entity.fileModifiedAt,
|
||||||
|
@ -4,7 +4,6 @@ import { ApiProperty } from '@nestjs/swagger';
|
|||||||
export class ExifResponseDto {
|
export class ExifResponseDto {
|
||||||
make?: string | null = null;
|
make?: string | null = null;
|
||||||
model?: string | null = null;
|
model?: string | null = null;
|
||||||
imageName?: string | null = null;
|
|
||||||
exifImageWidth?: number | null = null;
|
exifImageWidth?: number | null = null;
|
||||||
exifImageHeight?: number | null = null;
|
exifImageHeight?: number | null = null;
|
||||||
|
|
||||||
@ -30,7 +29,6 @@ export function mapExif(entity: ExifEntity): ExifResponseDto {
|
|||||||
return {
|
return {
|
||||||
make: entity.make,
|
make: entity.make,
|
||||||
model: entity.model,
|
model: entity.model,
|
||||||
imageName: entity.imageName,
|
|
||||||
exifImageWidth: entity.exifImageWidth,
|
exifImageWidth: entity.exifImageWidth,
|
||||||
exifImageHeight: entity.exifImageHeight,
|
exifImageHeight: entity.exifImageHeight,
|
||||||
fileSizeInByte: entity.fileSizeInByte ? parseInt(entity.fileSizeInByte.toString()) : null,
|
fileSizeInByte: entity.fileSizeInByte ? parseInt(entity.fileSizeInByte.toString()) : null,
|
||||||
|
@ -26,7 +26,7 @@ export class StorageTemplateService {
|
|||||||
const { asset } = data;
|
const { asset } = data;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const filename = asset.exifInfo?.imageName || asset.id;
|
const filename = asset.originalFileName || asset.id;
|
||||||
await this.moveAsset(asset, filename);
|
await this.moveAsset(asset, filename);
|
||||||
|
|
||||||
// move motion part of live photo
|
// move motion part of live photo
|
||||||
@ -56,7 +56,7 @@ export class StorageTemplateService {
|
|||||||
for (const asset of assets) {
|
for (const asset of assets) {
|
||||||
const livePhotoParentAsset = livePhotoMap[asset.id];
|
const livePhotoParentAsset = livePhotoMap[asset.id];
|
||||||
// TODO: remove livePhoto specific stuff once upload is fixed
|
// TODO: remove livePhoto specific stuff once upload is fixed
|
||||||
const filename = asset.exifInfo?.imageName || livePhotoParentAsset?.exifInfo?.imageName || asset.id;
|
const filename = asset.originalFileName || livePhotoParentAsset?.originalFileName || asset.id;
|
||||||
await this.moveAsset(asset, filename);
|
await this.moveAsset(asset, filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +118,7 @@ export const fileStub = {
|
|||||||
export const assetEntityStub = {
|
export const assetEntityStub = {
|
||||||
noResizePath: Object.freeze<AssetEntity>({
|
noResizePath: Object.freeze<AssetEntity>({
|
||||||
id: 'asset-id',
|
id: 'asset-id',
|
||||||
|
originalFileName: 'asset_1.jpeg',
|
||||||
deviceAssetId: 'device-asset-id',
|
deviceAssetId: 'device-asset-id',
|
||||||
fileModifiedAt: '2023-02-23T05:06:29.716Z',
|
fileModifiedAt: '2023-02-23T05:06:29.716Z',
|
||||||
fileCreatedAt: '2023-02-23T05:06:29.716Z',
|
fileCreatedAt: '2023-02-23T05:06:29.716Z',
|
||||||
@ -163,9 +164,11 @@ export const assetEntityStub = {
|
|||||||
livePhotoVideoId: null,
|
livePhotoVideoId: null,
|
||||||
tags: [],
|
tags: [],
|
||||||
sharedLinks: [],
|
sharedLinks: [],
|
||||||
|
originalFileName: 'asset-id.ext',
|
||||||
}),
|
}),
|
||||||
video: Object.freeze<AssetEntity>({
|
video: Object.freeze<AssetEntity>({
|
||||||
id: 'asset-id',
|
id: 'asset-id',
|
||||||
|
originalFileName: 'asset-id.ext',
|
||||||
deviceAssetId: 'device-asset-id',
|
deviceAssetId: 'device-asset-id',
|
||||||
fileModifiedAt: '2023-02-23T05:06:29.716Z',
|
fileModifiedAt: '2023-02-23T05:06:29.716Z',
|
||||||
fileCreatedAt: '2023-02-23T05:06:29.716Z',
|
fileCreatedAt: '2023-02-23T05:06:29.716Z',
|
||||||
@ -320,7 +323,6 @@ export const albumStub = {
|
|||||||
const assetInfo: ExifResponseDto = {
|
const assetInfo: ExifResponseDto = {
|
||||||
make: 'camera-make',
|
make: 'camera-make',
|
||||||
model: 'camera-model',
|
model: 'camera-model',
|
||||||
imageName: 'fancy-image',
|
|
||||||
exifImageWidth: 500,
|
exifImageWidth: 500,
|
||||||
exifImageHeight: 500,
|
exifImageHeight: 500,
|
||||||
fileSizeInByte: 100,
|
fileSizeInByte: 100,
|
||||||
@ -347,6 +349,7 @@ const assetResponse: AssetResponseDto = {
|
|||||||
deviceId: 'device_id_1',
|
deviceId: 'device_id_1',
|
||||||
type: AssetType.VIDEO,
|
type: AssetType.VIDEO,
|
||||||
originalPath: 'fake_path/jpeg',
|
originalPath: 'fake_path/jpeg',
|
||||||
|
originalFileName: 'asset_1.jpeg',
|
||||||
resizePath: '',
|
resizePath: '',
|
||||||
fileModifiedAt: today.toISOString(),
|
fileModifiedAt: today.toISOString(),
|
||||||
fileCreatedAt: today.toISOString(),
|
fileCreatedAt: today.toISOString(),
|
||||||
@ -602,6 +605,7 @@ export const sharedLinkStub = {
|
|||||||
isVisible: true,
|
isVisible: true,
|
||||||
livePhotoVideo: null,
|
livePhotoVideo: null,
|
||||||
livePhotoVideoId: null,
|
livePhotoVideoId: null,
|
||||||
|
originalFileName: 'asset_1.jpeg',
|
||||||
exifInfo: {
|
exifInfo: {
|
||||||
livePhotoCID: null,
|
livePhotoCID: null,
|
||||||
assetId: 'id_1',
|
assetId: 'id_1',
|
||||||
@ -620,7 +624,6 @@ export const sharedLinkStub = {
|
|||||||
country: 'country',
|
country: 'country',
|
||||||
make: 'camera-make',
|
make: 'camera-make',
|
||||||
model: 'camera-model',
|
model: 'camera-model',
|
||||||
imageName: 'fancy-image',
|
|
||||||
lensModel: 'fancy',
|
lensModel: 'fancy',
|
||||||
fNumber: 100,
|
fNumber: 100,
|
||||||
focalLength: 100,
|
focalLength: 100,
|
||||||
|
@ -87,6 +87,9 @@ export class AssetEntity {
|
|||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
livePhotoVideoId!: string | null;
|
livePhotoVideoId!: string | null;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar' })
|
||||||
|
originalFileName!: string;
|
||||||
|
|
||||||
@OneToOne(() => ExifEntity, (exifEntity) => exifEntity.asset)
|
@OneToOne(() => ExifEntity, (exifEntity) => exifEntity.asset)
|
||||||
exifInfo?: ExifEntity;
|
exifInfo?: ExifEntity;
|
||||||
|
|
||||||
|
@ -63,9 +63,6 @@ export class ExifEntity {
|
|||||||
@Column({ type: 'varchar', nullable: true })
|
@Column({ type: 'varchar', nullable: true })
|
||||||
model!: string | null;
|
model!: string | null;
|
||||||
|
|
||||||
@Column({ type: 'varchar', nullable: true })
|
|
||||||
imageName!: string | null;
|
|
||||||
|
|
||||||
@Column({ type: 'varchar', nullable: true })
|
@Column({ type: 'varchar', nullable: true })
|
||||||
lensModel!: string | null;
|
lensModel!: string | null;
|
||||||
|
|
||||||
@ -94,7 +91,6 @@ export class ExifEntity {
|
|||||||
COALESCE(model, '') || ' ' ||
|
COALESCE(model, '') || ' ' ||
|
||||||
COALESCE(orientation, '') || ' ' ||
|
COALESCE(orientation, '') || ' ' ||
|
||||||
COALESCE("lensModel", '') || ' ' ||
|
COALESCE("lensModel", '') || ' ' ||
|
||||||
COALESCE("imageName", '') || ' ' ||
|
|
||||||
COALESCE("city", '') || ' ' ||
|
COALESCE("city", '') || ' ' ||
|
||||||
COALESCE("state", '') || ' ' ||
|
COALESCE("state", '') || ' ' ||
|
||||||
COALESCE("country", ''))`,
|
COALESCE("country", ''))`,
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
export class AddOriginalFileNameToAssetTable1681144628393 implements MigrationInterface {
|
||||||
|
name = 'AddOriginalFileNameToAssetTable1681144628393';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "assets" ADD "originalFileName" character varying`);
|
||||||
|
|
||||||
|
await queryRunner.query(`
|
||||||
|
UPDATE assets a
|
||||||
|
SET "originalFileName" = (
|
||||||
|
select e."imageName"
|
||||||
|
from exif e
|
||||||
|
where e."assetId" = a.id
|
||||||
|
)
|
||||||
|
`);
|
||||||
|
|
||||||
|
await queryRunner.query(`
|
||||||
|
UPDATE assets a
|
||||||
|
SET "originalFileName" = a.id
|
||||||
|
where a."originalFileName" IS NULL or a."originalFileName" = ''
|
||||||
|
`);
|
||||||
|
|
||||||
|
await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "originalFileName" SET NOT NULL`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "originalFileName"`);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,62 @@
|
|||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
export class RemoveImageNameFromEXIFTable1681159594469 implements MigrationInterface {
|
||||||
|
name = 'RemoveImageNameFromEXIFTable1681159594469';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN IF EXISTS "exifTextSearchableColumn"`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "database" = $3 AND "schema" = $4 AND "table" = $5`,
|
||||||
|
['GENERATED_COLUMN', 'exifTextSearchableColumn', 'immich', 'public', 'exif'],
|
||||||
|
);
|
||||||
|
await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english',
|
||||||
|
COALESCE(make, '') || ' ' ||
|
||||||
|
COALESCE(model, '') || ' ' ||
|
||||||
|
COALESCE(orientation, '') || ' ' ||
|
||||||
|
COALESCE("lensModel", '') || ' ' ||
|
||||||
|
COALESCE("city", '') || ' ' ||
|
||||||
|
COALESCE("state", '') || ' ' ||
|
||||||
|
COALESCE("country", ''))) STORED NOT NULL`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES ($1, $2, $3, $4, $5, $6)`,
|
||||||
|
[
|
||||||
|
'immich',
|
||||||
|
'public',
|
||||||
|
'exif',
|
||||||
|
'GENERATED_COLUMN',
|
||||||
|
'exifTextSearchableColumn',
|
||||||
|
"TO_TSVECTOR('english',\n COALESCE(make, '') || ' ' ||\n COALESCE(model, '') || ' ' ||\n COALESCE(orientation, '') || ' ' ||\n COALESCE(\"lensModel\", '') || ' ' ||\n COALESCE(\"city\", '') || ' ' ||\n COALESCE(\"state\", '') || ' ' ||\n COALESCE(\"country\", ''))",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "imageName"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`DELETE FROM "typeorm_metadata" WHERE "type" = $1 AND "name" = $2 AND "database" = $3 AND "schema" = $4 AND "table" = $5`,
|
||||||
|
['GENERATED_COLUMN', 'exifTextSearchableColumn', 'immich', 'public', 'exif'],
|
||||||
|
);
|
||||||
|
await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "exifTextSearchableColumn"`);
|
||||||
|
await queryRunner.query(
|
||||||
|
`INSERT INTO "typeorm_metadata"("database", "schema", "table", "type", "name", "value") VALUES ($1, $2, $3, $4, $5, $6)`,
|
||||||
|
[
|
||||||
|
'immich',
|
||||||
|
'public',
|
||||||
|
'exif',
|
||||||
|
'GENERATED_COLUMN',
|
||||||
|
'exifTextSearchableColumn',
|
||||||
|
"TO_TSVECTOR('english',\n COALESCE(make, '') || ' ' ||\n COALESCE(model, '') || ' ' ||\n COALESCE(orientation, '') || ' ' ||\n COALESCE(\"lensModel\", '') || ' ' ||\n COALESCE(\"imageName\", '') || ' ' ||\n COALESCE(\"city\", '') || ' ' ||\n COALESCE(\"state\", '') || ' ' ||\n COALESCE(\"country\", ''))",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
await queryRunner.query(`ALTER TABLE "exif" ADD "exifTextSearchableColumn" tsvector GENERATED ALWAYS AS (TO_TSVECTOR('english',
|
||||||
|
COALESCE(make, '') || ' ' ||
|
||||||
|
COALESCE(model, '') || ' ' ||
|
||||||
|
COALESCE(orientation, '') || ' ' ||
|
||||||
|
COALESCE("lensModel", '') || ' ' ||
|
||||||
|
COALESCE("imageName", '') || ' ' ||
|
||||||
|
COALESCE("city", '') || ' ' ||
|
||||||
|
COALESCE("state", '') || ' ' ||
|
||||||
|
COALESCE("country", ''))) STORED NOT NULL`);
|
||||||
|
await queryRunner.query(`ALTER TABLE "exif" ADD "imageName" character varying`);
|
||||||
|
}
|
||||||
|
}
|
@ -144,7 +144,7 @@ export class TypesenseRepository implements ISearchRepository {
|
|||||||
|
|
||||||
const { facet_counts: facets } = await asset$.search({
|
const { facet_counts: facets } = await asset$.search({
|
||||||
...common,
|
...common,
|
||||||
query_by: 'exifInfo.imageName',
|
query_by: 'originalFileName',
|
||||||
facet_by: 'exifInfo.city,smartInfo.objects',
|
facet_by: 'exifInfo.city,smartInfo.objects',
|
||||||
max_facet_values: 12,
|
max_facet_values: 12,
|
||||||
});
|
});
|
||||||
@ -157,7 +157,7 @@ export class TypesenseRepository implements ISearchRepository {
|
|||||||
mergeMap((count) => {
|
mergeMap((count) => {
|
||||||
const config = {
|
const config = {
|
||||||
...common,
|
...common,
|
||||||
query_by: 'exifInfo.imageName',
|
query_by: 'originalFileName',
|
||||||
filter_by: [
|
filter_by: [
|
||||||
this.buildFilterBy('ownerId', userId, true),
|
this.buildFilterBy('ownerId', userId, true),
|
||||||
this.buildFilterBy(facet.field_name, count.value, true),
|
this.buildFilterBy(facet.field_name, count.value, true),
|
||||||
@ -230,7 +230,7 @@ export class TypesenseRepository implements ISearchRepository {
|
|||||||
.search({
|
.search({
|
||||||
q: query,
|
q: query,
|
||||||
query_by: [
|
query_by: [
|
||||||
'exifInfo.imageName',
|
'originalFileName',
|
||||||
'exifInfo.country',
|
'exifInfo.country',
|
||||||
'exifInfo.state',
|
'exifInfo.state',
|
||||||
'exifInfo.city',
|
'exifInfo.city',
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { CollectionCreateSchema } from 'typesense/lib/Typesense/Collections';
|
import { CollectionCreateSchema } from 'typesense/lib/Typesense/Collections';
|
||||||
|
|
||||||
export const assetSchemaVersion = 3;
|
export const assetSchemaVersion = 4;
|
||||||
export const assetSchema: CollectionCreateSchema = {
|
export const assetSchema: CollectionCreateSchema = {
|
||||||
name: `assets-v${assetSchemaVersion}`,
|
name: `assets-v${assetSchemaVersion}`,
|
||||||
fields: [
|
fields: [
|
||||||
@ -13,6 +13,7 @@ export const assetSchema: CollectionCreateSchema = {
|
|||||||
{ name: 'fileCreatedAt', type: 'string', facet: false, sort: true },
|
{ name: 'fileCreatedAt', type: 'string', facet: false, sort: true },
|
||||||
{ name: 'fileModifiedAt', type: 'string', facet: false, sort: true },
|
{ name: 'fileModifiedAt', type: 'string', facet: false, sort: true },
|
||||||
{ name: 'isFavorite', type: 'bool', facet: true },
|
{ name: 'isFavorite', type: 'bool', facet: true },
|
||||||
|
{ name: 'originalFileName', type: 'string', facet: false, optional: true },
|
||||||
// { name: 'checksum', type: 'string', facet: true },
|
// { name: 'checksum', type: 'string', facet: true },
|
||||||
// { name: 'tags', type: 'string[]', facet: true, optional: true },
|
// { name: 'tags', type: 'string[]', facet: true, optional: true },
|
||||||
|
|
||||||
@ -21,7 +22,6 @@ export const assetSchema: CollectionCreateSchema = {
|
|||||||
{ name: 'exifInfo.country', type: 'string', facet: true, optional: true },
|
{ name: 'exifInfo.country', type: 'string', facet: true, optional: true },
|
||||||
{ name: 'exifInfo.state', type: 'string', facet: true, optional: true },
|
{ name: 'exifInfo.state', type: 'string', facet: true, optional: true },
|
||||||
{ name: 'exifInfo.description', type: 'string', facet: false, optional: true },
|
{ name: 'exifInfo.description', type: 'string', facet: false, optional: true },
|
||||||
{ name: 'exifInfo.imageName', type: 'string', facet: false, optional: true },
|
|
||||||
{ name: 'exifInfo.make', type: 'string', facet: true, optional: true },
|
{ name: 'exifInfo.make', type: 'string', facet: true, optional: true },
|
||||||
{ name: 'exifInfo.model', type: 'string', facet: true, optional: true },
|
{ name: 'exifInfo.model', type: 'string', facet: true, optional: true },
|
||||||
{ name: 'exifInfo.orientation', type: 'string', optional: true },
|
{ name: 'exifInfo.orientation', type: 'string', optional: true },
|
||||||
|
12
web/src/api/open-api/api.ts
generated
12
web/src/api/open-api/api.ts
generated
@ -476,6 +476,12 @@ export interface AssetResponseDto {
|
|||||||
* @memberof AssetResponseDto
|
* @memberof AssetResponseDto
|
||||||
*/
|
*/
|
||||||
'originalPath': string;
|
'originalPath': string;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
* @memberof AssetResponseDto
|
||||||
|
*/
|
||||||
|
'originalFileName': string;
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {string}
|
* @type {string}
|
||||||
@ -1100,12 +1106,6 @@ export interface ExifResponseDto {
|
|||||||
* @memberof ExifResponseDto
|
* @memberof ExifResponseDto
|
||||||
*/
|
*/
|
||||||
'model'?: string | null;
|
'model'?: string | null;
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {string}
|
|
||||||
* @memberof ExifResponseDto
|
|
||||||
*/
|
|
||||||
'imageName'?: string | null;
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @type {number}
|
* @type {number}
|
||||||
|
@ -96,7 +96,7 @@
|
|||||||
<div><ImageOutline size="24" /></div>
|
<div><ImageOutline size="24" /></div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p>{`${asset.exifInfo.imageName}.${asset.originalPath.split('.')[1]}` || ''}</p>
|
<p>{`${asset.originalFileName}.${asset.originalPath.split('.')[1]}` || ''}</p>
|
||||||
<div class="flex text-sm gap-2">
|
<div class="flex text-sm gap-2">
|
||||||
{#if asset.exifInfo.exifImageHeight && asset.exifInfo.exifImageWidth}
|
{#if asset.exifInfo.exifImageHeight && asset.exifInfo.exifImageWidth}
|
||||||
{#if getMegapixel(asset.exifInfo.exifImageHeight, asset.exifInfo.exifImageWidth)}
|
{#if getMegapixel(asset.exifInfo.exifImageHeight, asset.exifInfo.exifImageWidth)}
|
||||||
|
@ -116,7 +116,7 @@
|
|||||||
|
|
||||||
<ImageThumbnail
|
<ImageThumbnail
|
||||||
url={api.getAssetThumbnailUrl(asset.id, format, publicSharedKey)}
|
url={api.getAssetThumbnailUrl(asset.id, format, publicSharedKey)}
|
||||||
altText={asset.exifInfo?.imageName ?? asset.id}
|
altText={asset.originalFileName}
|
||||||
widthStyle="{width}px"
|
widthStyle="{width}px"
|
||||||
heightStyle="{height}px"
|
heightStyle="{height}px"
|
||||||
/>
|
/>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user