diff --git a/mobile/lib/shared/models/asset.dart b/mobile/lib/shared/models/asset.dart index 0bee110f8a..840653554e 100644 --- a/mobile/lib/shared/models/asset.dart +++ b/mobile/lib/shared/models/asset.dart @@ -15,9 +15,9 @@ class Asset { Asset.remote(AssetResponseDto remote) : remoteId = remote.id, isLocal = false, - fileCreatedAt = DateTime.parse(remote.fileCreatedAt), - fileModifiedAt = DateTime.parse(remote.fileModifiedAt), - updatedAt = DateTime.parse(remote.updatedAt), + fileCreatedAt = remote.fileCreatedAt, + fileModifiedAt = remote.fileModifiedAt, + updatedAt = remote.updatedAt, durationInSeconds = remote.duration.toDuration()?.inSeconds ?? 0, type = remote.type.toAssetType(), fileName = p.basename(remote.originalPath), diff --git a/mobile/openapi/doc/AssetApi.md b/mobile/openapi/doc/AssetApi.md index 9bee8759ac..9a1c073406 100644 Binary files a/mobile/openapi/doc/AssetApi.md and b/mobile/openapi/doc/AssetApi.md differ diff --git a/mobile/openapi/doc/AssetResponseDto.md b/mobile/openapi/doc/AssetResponseDto.md index e4cef33ec3..f64e7144f6 100644 Binary files a/mobile/openapi/doc/AssetResponseDto.md and b/mobile/openapi/doc/AssetResponseDto.md differ diff --git a/mobile/openapi/lib/api/asset_api.dart b/mobile/openapi/lib/api/asset_api.dart index 609a6536cd..e27d4e4557 100644 Binary files a/mobile/openapi/lib/api/asset_api.dart and b/mobile/openapi/lib/api/asset_api.dart differ diff --git a/mobile/openapi/lib/model/asset_response_dto.dart b/mobile/openapi/lib/model/asset_response_dto.dart index 664a5a76f7..22f6f5f5d1 100644 Binary files a/mobile/openapi/lib/model/asset_response_dto.dart and b/mobile/openapi/lib/model/asset_response_dto.dart differ diff --git a/mobile/openapi/test/asset_api_test.dart b/mobile/openapi/test/asset_api_test.dart index e9615e2d50..e5d50815ca 100644 Binary files a/mobile/openapi/test/asset_api_test.dart and b/mobile/openapi/test/asset_api_test.dart differ diff --git a/mobile/openapi/test/asset_response_dto_test.dart b/mobile/openapi/test/asset_response_dto_test.dart index fb446bd21a..34b31d510e 100644 Binary files a/mobile/openapi/test/asset_response_dto_test.dart and b/mobile/openapi/test/asset_response_dto_test.dart differ diff --git a/server/apps/immich/src/api-v1/asset/asset.service.spec.ts b/server/apps/immich/src/api-v1/asset/asset.service.spec.ts index 98838de378..abe63eac7e 100644 --- a/server/apps/immich/src/api-v1/asset/asset.service.spec.ts +++ b/server/apps/immich/src/api-v1/asset/asset.service.spec.ts @@ -37,8 +37,8 @@ const _getCreateAssetDto = (): CreateAssetDto => { createAssetDto.deviceAssetId = 'deviceAssetId'; createAssetDto.deviceId = 'deviceId'; createAssetDto.assetType = AssetType.OTHER; - createAssetDto.fileCreatedAt = '2022-06-19T23:41:36.910Z'; - createAssetDto.fileModifiedAt = '2022-06-19T23:41:36.910Z'; + createAssetDto.fileCreatedAt = new Date('2022-06-19T23:41:36.910Z'); + createAssetDto.fileModifiedAt = new Date('2022-06-19T23:41:36.910Z'); createAssetDto.isFavorite = false; createAssetDto.isArchived = false; createAssetDto.duration = '0:00:00.000000'; @@ -56,9 +56,9 @@ const _getAsset_1 = () => { asset_1.type = AssetType.VIDEO; asset_1.originalPath = 'fake_path/asset_1.jpeg'; asset_1.resizePath = ''; - asset_1.fileModifiedAt = '2022-06-19T23:41:36.910Z'; - asset_1.fileCreatedAt = '2022-06-19T23:41:36.910Z'; - asset_1.updatedAt = '2022-06-19T23:41:36.910Z'; + asset_1.fileModifiedAt = new Date('2022-06-19T23:41:36.910Z'); + asset_1.fileCreatedAt = new Date('2022-06-19T23:41:36.910Z'); + asset_1.updatedAt = new Date('2022-06-19T23:41:36.910Z'); asset_1.isFavorite = false; asset_1.isArchived = false; asset_1.mimeType = 'image/jpeg'; @@ -81,9 +81,9 @@ const _getAsset_2 = () => { asset_2.type = AssetType.VIDEO; asset_2.originalPath = 'fake_path/asset_2.jpeg'; asset_2.resizePath = ''; - asset_2.fileModifiedAt = '2022-06-19T23:41:36.910Z'; - asset_2.fileCreatedAt = '2022-06-19T23:41:36.910Z'; - asset_2.updatedAt = '2022-06-19T23:41:36.910Z'; + asset_2.fileModifiedAt = new Date('2022-06-19T23:41:36.910Z'); + asset_2.fileCreatedAt = new Date('2022-06-19T23:41:36.910Z'); + asset_2.updatedAt = new Date('2022-06-19T23:41:36.910Z'); asset_2.isFavorite = false; asset_2.isArchived = false; asset_2.mimeType = 'image/jpeg'; diff --git a/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts b/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts index 582b1249aa..1c4880fc80 100644 --- a/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts +++ b/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts @@ -16,10 +16,10 @@ export class CreateAssetDto { assetType!: AssetType; @IsNotEmpty() - fileCreatedAt!: string; + fileCreatedAt!: Date; @IsNotEmpty() - fileModifiedAt!: string; + fileModifiedAt!: Date; @IsNotEmpty() isFavorite!: boolean; diff --git a/server/apps/microservices/src/processors/metadata-extraction.processor.ts b/server/apps/microservices/src/processors/metadata-extraction.processor.ts index af8767a35b..8395b9fdad 100644 --- a/server/apps/microservices/src/processors/metadata-extraction.processor.ts +++ b/server/apps/microservices/src/processors/metadata-extraction.processor.ts @@ -214,7 +214,7 @@ export class MetadataExtractionProcessor { } await this.exifRepository.upsert(newExif, { conflictPaths: ['assetId'] }); - await this.assetRepository.save({ id: asset.id, fileCreatedAt: fileCreatedAt?.toISOString() }); + await this.assetRepository.save({ id: asset.id, fileCreatedAt: fileCreatedAt || undefined }); return true; } @@ -227,9 +227,9 @@ export class MetadataExtractionProcessor { const videoTags = data.format.tags; if (videoTags) { if (videoTags['com.apple.quicktime.creationdate']) { - fileCreatedAt = String(videoTags['com.apple.quicktime.creationdate']); + fileCreatedAt = new Date(videoTags['com.apple.quicktime.creationdate']); } else if (videoTags['creation_time']) { - fileCreatedAt = String(videoTags['creation_time']); + fileCreatedAt = new Date(videoTags['creation_time']); } } diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index c795ef0f78..2fbf1a22a4 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -4450,12 +4450,15 @@ "type": "boolean" }, "fileCreatedAt": { + "format": "date-time", "type": "string" }, "fileModifiedAt": { + "format": "date-time", "type": "string" }, "updatedAt": { + "format": "date-time", "type": "string" }, "isFavorite": { @@ -5783,9 +5786,11 @@ "type": "string" }, "fileCreatedAt": { + "format": "date-time", "type": "string" }, "fileModifiedAt": { + "format": "date-time", "type": "string" }, "isFavorite": { diff --git a/server/libs/domain/src/asset/asset.repository.ts b/server/libs/domain/src/asset/asset.repository.ts index efdfe2eca0..21f88a0c8e 100644 --- a/server/libs/domain/src/asset/asset.repository.ts +++ b/server/libs/domain/src/asset/asset.repository.ts @@ -15,8 +15,8 @@ export interface LivePhotoSearchOptions { export interface MapMarkerSearchOptions { isFavorite?: boolean; - fileCreatedBefore?: string; - fileCreatedAfter?: string; + fileCreatedBefore?: Date; + fileCreatedAfter?: Date; } export interface MapMarker { diff --git a/server/libs/domain/src/asset/dto/map-marker.dto.ts b/server/libs/domain/src/asset/dto/map-marker.dto.ts index 1fef60c16e..5398b95472 100644 --- a/server/libs/domain/src/asset/dto/map-marker.dto.ts +++ b/server/libs/domain/src/asset/dto/map-marker.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { toBoolean } from 'apps/immich/src/utils/transform.util'; -import { Transform } from 'class-transformer'; -import { IsBoolean, IsISO8601, IsOptional } from 'class-validator'; +import { Transform, Type } from 'class-transformer'; +import { IsBoolean, IsDate, IsOptional } from 'class-validator'; export class MapMarkerDto { @ApiProperty() @@ -10,13 +10,13 @@ export class MapMarkerDto { @Transform(toBoolean) isFavorite?: boolean; - @ApiProperty({ format: 'date-time' }) @IsOptional() - @IsISO8601({ strict: true, strictSeparator: true }) - fileCreatedAfter?: string; + @IsDate() + @Type(() => Date) + fileCreatedAfter?: Date; - @ApiProperty({ format: 'date-time' }) @IsOptional() - @IsISO8601({ strict: true, strictSeparator: true }) - fileCreatedBefore?: string; + @IsDate() + @Type(() => Date) + fileCreatedBefore?: Date; } diff --git a/server/libs/domain/src/asset/response-dto/asset-response.dto.ts b/server/libs/domain/src/asset/response-dto/asset-response.dto.ts index 8a17466132..4ece34bcd6 100644 --- a/server/libs/domain/src/asset/response-dto/asset-response.dto.ts +++ b/server/libs/domain/src/asset/response-dto/asset-response.dto.ts @@ -16,9 +16,9 @@ export class AssetResponseDto { originalPath!: string; originalFileName!: string; resized!: boolean; - fileCreatedAt!: string; - fileModifiedAt!: string; - updatedAt!: string; + fileCreatedAt!: Date; + fileModifiedAt!: Date; + updatedAt!: Date; isFavorite!: boolean; isArchived!: boolean; mimeType!: string | null; diff --git a/server/libs/domain/src/storage-template/storage-template.core.ts b/server/libs/domain/src/storage-template/storage-template.core.ts index 85c3d12b2b..da97ab6294 100644 --- a/server/libs/domain/src/storage-template/storage-template.core.ts +++ b/server/libs/domain/src/storage-template/storage-template.core.ts @@ -107,7 +107,7 @@ export class StorageTemplateCore { this.render( template, { - fileCreatedAt: new Date().toISOString(), + fileCreatedAt: new Date(), originalPath: '/upload/test/IMG_123.jpg', type: AssetType.IMAGE, } as AssetEntity, @@ -140,7 +140,7 @@ export class StorageTemplateCore { filetypefull: asset.type == AssetType.IMAGE ? 'IMAGE' : 'VIDEO', }; - const dt = luxon.DateTime.fromISO(new Date(asset.fileCreatedAt).toISOString()); + const dt = luxon.DateTime.fromJSDate(asset.fileCreatedAt); const dateTokens = [ ...supportedYearTokens, diff --git a/server/libs/domain/test/fixtures.ts b/server/libs/domain/test/fixtures.ts index 1a1753352c..da420346fe 100644 --- a/server/libs/domain/test/fixtures.ts +++ b/server/libs/domain/test/fixtures.ts @@ -140,8 +140,8 @@ export const assetEntityStub = { id: 'asset-id', originalFileName: 'asset_1.jpeg', deviceAssetId: 'device-asset-id', - fileModifiedAt: '2023-02-23T05:06:29.716Z', - fileCreatedAt: '2023-02-23T05:06:29.716Z', + fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'), + fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'), owner: userEntityStub.user1, ownerId: 'user-id', deviceId: 'device-id', @@ -151,8 +151,8 @@ export const assetEntityStub = { type: AssetType.IMAGE, webpPath: null, encodedVideoPath: null, - createdAt: '2023-02-23T05:06:29.716Z', - updatedAt: '2023-02-23T05:06:29.716Z', + createdAt: new Date('2023-02-23T05:06:29.716Z'), + updatedAt: new Date('2023-02-23T05:06:29.716Z'), mimeType: null, isFavorite: true, isArchived: false, @@ -168,8 +168,8 @@ export const assetEntityStub = { image: Object.freeze({ id: 'asset-id', deviceAssetId: 'device-asset-id', - fileModifiedAt: '2023-02-23T05:06:29.716Z', - fileCreatedAt: '2023-02-23T05:06:29.716Z', + fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'), + fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'), owner: userEntityStub.user1, ownerId: 'user-id', deviceId: 'device-id', @@ -179,8 +179,8 @@ export const assetEntityStub = { type: AssetType.IMAGE, webpPath: null, encodedVideoPath: null, - createdAt: '2023-02-23T05:06:29.716Z', - updatedAt: '2023-02-23T05:06:29.716Z', + createdAt: new Date('2023-02-23T05:06:29.716Z'), + updatedAt: new Date('2023-02-23T05:06:29.716Z'), mimeType: null, isFavorite: true, isArchived: false, @@ -198,8 +198,8 @@ export const assetEntityStub = { id: 'asset-id', originalFileName: 'asset-id.ext', deviceAssetId: 'device-asset-id', - fileModifiedAt: '2023-02-23T05:06:29.716Z', - fileCreatedAt: '2023-02-23T05:06:29.716Z', + fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'), + fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'), owner: userEntityStub.user1, ownerId: 'user-id', deviceId: 'device-id', @@ -209,8 +209,8 @@ export const assetEntityStub = { type: AssetType.VIDEO, webpPath: null, encodedVideoPath: null, - createdAt: '2023-02-23T05:06:29.716Z', - updatedAt: '2023-02-23T05:06:29.716Z', + createdAt: new Date('2023-02-23T05:06:29.716Z'), + updatedAt: new Date('2023-02-23T05:06:29.716Z'), mimeType: null, isFavorite: true, isArchived: false, @@ -229,8 +229,8 @@ export const assetEntityStub = { ownerId: authStub.user1.id, type: AssetType.VIDEO, isVisible: false, - fileModifiedAt: '2022-06-19T23:41:36.910Z', - fileCreatedAt: '2022-06-19T23:41:36.910Z', + fileModifiedAt: new Date('2022-06-19T23:41:36.910Z'), + fileCreatedAt: new Date('2022-06-19T23:41:36.910Z'), } as AssetEntity), livePhotoStillAsset: Object.freeze({ @@ -240,15 +240,15 @@ export const assetEntityStub = { type: AssetType.IMAGE, livePhotoVideoId: 'live-photo-motion-asset', isVisible: true, - fileModifiedAt: '2022-06-19T23:41:36.910Z', - fileCreatedAt: '2022-06-19T23:41:36.910Z', + fileModifiedAt: new Date('2022-06-19T23:41:36.910Z'), + fileCreatedAt: new Date('2022-06-19T23:41:36.910Z'), } as AssetEntity), withLocation: Object.freeze({ id: 'asset-with-favorite-id', deviceAssetId: 'device-asset-id', - fileModifiedAt: '2023-02-23T05:06:29.716Z', - fileCreatedAt: '2023-02-23T05:06:29.716Z', + fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'), + fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'), owner: userEntityStub.user1, ownerId: 'user-id', deviceId: 'device-id', @@ -259,8 +259,8 @@ export const assetEntityStub = { type: AssetType.IMAGE, webpPath: null, encodedVideoPath: null, - createdAt: '2023-02-23T05:06:29.716Z', - updatedAt: '2023-02-23T05:06:29.716Z', + createdAt: new Date('2023-02-23T05:06:29.716Z'), + updatedAt: new Date('2023-02-23T05:06:29.716Z'), mimeType: null, isFavorite: false, isArchived: false, @@ -280,8 +280,8 @@ export const assetEntityStub = { sidecar: Object.freeze({ id: 'asset-id', deviceAssetId: 'device-asset-id', - fileModifiedAt: '2023-02-23T05:06:29.716Z', - fileCreatedAt: '2023-02-23T05:06:29.716Z', + fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'), + fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'), owner: userEntityStub.user1, ownerId: 'user-id', deviceId: 'device-id', @@ -291,8 +291,8 @@ export const assetEntityStub = { type: AssetType.IMAGE, webpPath: null, encodedVideoPath: null, - createdAt: '2023-02-23T05:06:29.716Z', - updatedAt: '2023-02-23T05:06:29.716Z', + createdAt: new Date('2023-02-23T05:06:29.716Z'), + updatedAt: new Date('2023-02-23T05:06:29.716Z'), mimeType: null, isFavorite: true, isArchived: false, @@ -447,9 +447,9 @@ const assetResponse: AssetResponseDto = { originalPath: 'fake_path/jpeg', originalFileName: 'asset_1.jpeg', resized: false, - fileModifiedAt: today.toISOString(), - fileCreatedAt: today.toISOString(), - updatedAt: today.toISOString(), + fileModifiedAt: today, + fileCreatedAt: today, + updatedAt: today, isFavorite: false, isArchived: false, mimeType: 'image/jpeg', @@ -700,10 +700,10 @@ export const sharedLinkStub = { originalPath: 'fake_path/jpeg', resizePath: '', checksum: Buffer.from('file hash', 'utf8'), - fileModifiedAt: today.toISOString(), - fileCreatedAt: today.toISOString(), - createdAt: today.toISOString(), - updatedAt: today.toISOString(), + fileModifiedAt: today, + fileCreatedAt: today, + createdAt: today, + updatedAt: today, isFavorite: false, isArchived: false, mimeType: 'image/jpeg', diff --git a/server/libs/infra/src/entities/asset.entity.ts b/server/libs/infra/src/entities/asset.entity.ts index 58ea69565e..202e393d22 100644 --- a/server/libs/infra/src/entities/asset.entity.ts +++ b/server/libs/infra/src/entities/asset.entity.ts @@ -55,16 +55,16 @@ export class AssetEntity { encodedVideoPath!: string | null; @CreateDateColumn({ type: 'timestamptz' }) - createdAt!: string; + createdAt!: Date; @UpdateDateColumn({ type: 'timestamptz' }) - updatedAt!: string; + updatedAt!: Date; @Column({ type: 'timestamptz' }) - fileCreatedAt!: string; + fileCreatedAt!: Date; @Column({ type: 'timestamptz' }) - fileModifiedAt!: string; + fileModifiedAt!: Date; @Column({ type: 'boolean', default: false }) isFavorite!: boolean; diff --git a/server/package.json b/server/package.json index f3f7722720..5a4f1bf391 100644 --- a/server/package.json +++ b/server/package.json @@ -141,7 +141,7 @@ "./libs/domain/": { "branches": 80, "functions": 87, - "lines": 93.8, + "lines": 93.7, "statements": 93 } },