mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-26 17:37:44 +02:00
✨ feat(photos): add Photos app with mana-media EXIF integration
- Add Photos NestJS backend (port 3019) with albums, favorites, tags - Add Photos SvelteKit web app (port 5189) with gallery, upload, filters - Extend mana-media with EXIF extraction service using exifr - Add cross-app photo listing endpoint to mana-media - Add photo stats endpoint to mana-media - Add photos to setup-databases.sh Backend features: - Albums CRUD with cover image and items management - Favorites toggle with status check - Tags CRUD with photo-tag associations - Photo proxy to mana-media with local data enrichment Web features: - Photo grid with infinite scroll - Photo detail modal with EXIF display - Album grid and detail views - Upload dropzone with progress tracking - Filter bar (app, date range, location, sort) - i18n support (de/en) - Svelte 5 runes mode Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
d3392f69a9
commit
90c2f8573e
80 changed files with 6891 additions and 503 deletions
|
|
@ -4,6 +4,8 @@ import { ProcessService } from './process.service';
|
|||
import { ProcessWorker } from './process.worker';
|
||||
import { PROCESS_QUEUE } from './process.constants';
|
||||
import { UploadModule } from '../upload/upload.module';
|
||||
import { ExifModule } from '../exif/exif.module';
|
||||
import { StorageModule } from '../storage/storage.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
|
@ -11,6 +13,8 @@ import { UploadModule } from '../upload/upload.module';
|
|||
name: PROCESS_QUEUE,
|
||||
}),
|
||||
forwardRef(() => UploadModule),
|
||||
ExifModule,
|
||||
StorageModule,
|
||||
],
|
||||
providers: [ProcessService, ProcessWorker],
|
||||
exports: [ProcessService],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import sharp from 'sharp';
|
||||
import { StorageService } from '../storage/storage.service';
|
||||
import { ExifService, type ExifData } from '../exif/exif.service';
|
||||
import { IMAGE_VARIANTS, SUPPORTED_IMAGE_TYPES } from './process.constants';
|
||||
|
||||
export interface ProcessResult {
|
||||
|
|
@ -13,11 +14,15 @@ export interface ProcessResult {
|
|||
format?: string;
|
||||
hasAlpha?: boolean;
|
||||
};
|
||||
exif?: ExifData;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ProcessService {
|
||||
constructor(private storage: StorageService) {}
|
||||
constructor(
|
||||
private storage: StorageService,
|
||||
private exifService: ExifService
|
||||
) {}
|
||||
|
||||
async processImage(
|
||||
mediaId: string,
|
||||
|
|
@ -35,6 +40,9 @@ export class ProcessService {
|
|||
const image = sharp(originalBuffer);
|
||||
const metadata = await image.metadata();
|
||||
|
||||
// Extract EXIF data
|
||||
const exifData = await this.exifService.extract(originalBuffer);
|
||||
|
||||
const result: ProcessResult = {
|
||||
metadata: {
|
||||
width: metadata.width,
|
||||
|
|
@ -42,6 +50,7 @@ export class ProcessService {
|
|||
format: metadata.format,
|
||||
hasAlpha: metadata.hasAlpha,
|
||||
},
|
||||
exif: exifData || undefined,
|
||||
};
|
||||
|
||||
// Generate variants
|
||||
|
|
|
|||
|
|
@ -57,10 +57,21 @@ export class ProcessWorker extends WorkerHost {
|
|||
height: result.metadata?.height,
|
||||
format: result.metadata?.format,
|
||||
hasAlpha: result.metadata?.hasAlpha,
|
||||
// EXIF data
|
||||
exifData: result.exif?.raw,
|
||||
dateTaken: result.exif?.dateTaken,
|
||||
cameraMake: result.exif?.cameraMake,
|
||||
cameraModel: result.exif?.cameraModel,
|
||||
focalLength: result.exif?.focalLength,
|
||||
aperture: result.exif?.aperture,
|
||||
iso: result.exif?.iso,
|
||||
exposureTime: result.exif?.exposureTime,
|
||||
gpsLatitude: result.exif?.gpsLatitude,
|
||||
gpsLongitude: result.exif?.gpsLongitude,
|
||||
});
|
||||
|
||||
this.logger.log(
|
||||
`Processed image ${mediaId}: thumbnail=${!!result.thumbnail}, medium=${!!result.medium}, large=${!!result.large}`
|
||||
`Processed image ${mediaId}: thumbnail=${!!result.thumbnail}, medium=${!!result.medium}, large=${!!result.large}, exif=${!!result.exif}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue