mirror of
https://github.com/KevinMidboe/immich.git
synced 2025-12-08 20:29:05 +00:00
fix: use local time for time buckets and improve memories (#4072)
* fix: timezone bucket timezones * chore: open api * fix: interpret local time in utc * fix: tests * fix: refactor memory lane * fix(web): use local date in memory viewer * chore: set localDateTime non-null * fix: filter out memories from the current year * wip: move localDateTime to asset * fix: correct sorting from db * fix: migration * fix: web typo * fix: formatting * fix: e2e * chore: localDateTime is non-null * chore: more non-nulliness * fix: asset stub * fix: tests * fix: use extract and index for day of year * fix: don't show memories before today * fix: cleanup * fix: tests * fix: only use localtime for tz * fix: display memories in client timezone * fix: tests * fix: svelte tests * fix: bugs * chore: open api --------- Co-authored-by: Jonathan Jogenfors <jonathan@jogenfors.se>
This commit is contained in:
@@ -28,6 +28,8 @@ export const ASSET_CHECKSUM_CONSTRAINT = 'UQ_assets_owner_library_checksum';
|
||||
@Index(ASSET_CHECKSUM_CONSTRAINT, ['owner', 'library', 'checksum'], {
|
||||
unique: true,
|
||||
})
|
||||
@Index('IDX_day_of_month', { synchronize: false })
|
||||
@Index('IDX_month', { synchronize: false })
|
||||
// For all assets, each originalpath must be unique per user and library
|
||||
export class AssetEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
@@ -78,6 +80,9 @@ export class AssetEntity {
|
||||
@Column({ type: 'timestamptz' })
|
||||
fileCreatedAt!: Date;
|
||||
|
||||
@Column({ type: 'timestamp' })
|
||||
localDateTime!: Date;
|
||||
|
||||
@Column({ type: 'timestamptz' })
|
||||
fileModifiedAt!: Date;
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddLocalDateTime1694525143117 implements MigrationInterface {
|
||||
name = 'AddLocalDateTime1694525143117';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "assets" ADD "localDateTime" TIMESTAMP`);
|
||||
await queryRunner.query(`
|
||||
update "assets"
|
||||
set "localDateTime" = "fileCreatedAt"`);
|
||||
|
||||
await queryRunner.query(`
|
||||
update "assets"
|
||||
set "localDateTime" = "fileCreatedAt" at TIME ZONE "exif"."timeZone"
|
||||
from "exif"
|
||||
where
|
||||
"exif"."assetId" = "assets"."id" and
|
||||
"exif"."timeZone" is not null`);
|
||||
|
||||
await queryRunner.query(`ALTER TABLE "assets" ALTER COLUMN "localDateTime" SET NOT NULL`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_day_of_month" ON assets (EXTRACT(DAY FROM "localDateTime"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_month" ON assets (EXTRACT(MONTH FROM "localDateTime"))`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "assets" DROP COLUMN "localDateTime"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_day_of_month"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_month"`);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
AssetCreate,
|
||||
AssetSearchOptions,
|
||||
AssetStats,
|
||||
AssetStatsOptions,
|
||||
@@ -6,6 +7,7 @@ import {
|
||||
LivePhotoSearchOptions,
|
||||
MapMarker,
|
||||
MapMarkerSearchOptions,
|
||||
MonthDay,
|
||||
Paginated,
|
||||
PaginationOptions,
|
||||
TimeBucketItem,
|
||||
@@ -38,9 +40,7 @@ export class AssetRepository implements IAssetRepository {
|
||||
await this.exifRepository.upsert(exif, { conflictPaths: ['assetId'] });
|
||||
}
|
||||
|
||||
create(
|
||||
asset: Omit<AssetEntity, 'id' | 'createdAt' | 'updatedAt' | 'ownerId' | 'livePhotoVideoId'>,
|
||||
): Promise<AssetEntity> {
|
||||
create(asset: AssetCreate): Promise<AssetEntity> {
|
||||
return this.repository.save(asset);
|
||||
}
|
||||
|
||||
@@ -78,6 +78,26 @@ export class AssetRepository implements IAssetRepository {
|
||||
});
|
||||
}
|
||||
|
||||
getByDayOfYear(ownerId: string, { day, month }: MonthDay): Promise<AssetEntity[]> {
|
||||
return this.repository
|
||||
.createQueryBuilder('entity')
|
||||
.where(
|
||||
`entity.ownerId = :ownerId
|
||||
AND entity.isVisible = true
|
||||
AND entity.isArchived = false
|
||||
AND entity.resizePath IS NOT NULL
|
||||
AND EXTRACT(DAY FROM entity.localDateTime) = :day
|
||||
AND EXTRACT(MONTH FROM entity.localDateTime) = :month`,
|
||||
{
|
||||
ownerId,
|
||||
day,
|
||||
month,
|
||||
},
|
||||
)
|
||||
.orderBy('entity.localDateTime', 'DESC')
|
||||
.getMany();
|
||||
}
|
||||
|
||||
getByIds(ids: string[]): Promise<AssetEntity[]> {
|
||||
return this.repository.find({
|
||||
where: { id: In(ids) },
|
||||
@@ -454,8 +474,9 @@ export class AssetRepository implements IAssetRepository {
|
||||
getByTimeBucket(timeBucket: string, options: TimeBucketOptions): Promise<AssetEntity[]> {
|
||||
const truncateValue = truncateMap[options.size];
|
||||
return this.getBuilder(options)
|
||||
.andWhere(`date_trunc('${truncateValue}', "fileCreatedAt") = :timeBucket`, { timeBucket })
|
||||
.orderBy('asset.fileCreatedAt', 'DESC')
|
||||
.andWhere(`date_trunc('${truncateValue}', "localDateTime") = :timeBucket`, { timeBucket })
|
||||
.orderBy(`date_trunc('day', "localDateTime")`, 'DESC')
|
||||
.addOrderBy('asset.fileCreatedAt', 'DESC')
|
||||
.getMany();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user