diff --git a/backend/src/layers/httpexception/httpexception.filter.ts b/backend/src/layers/httpexception/httpexception.filter.ts index d086188..6cff6e2 100644 --- a/backend/src/layers/httpexception/httpexception.filter.ts +++ b/backend/src/layers/httpexception/httpexception.filter.ts @@ -2,7 +2,8 @@ import { ArgumentsHost, Catch, ExceptionFilter, - HttpException + HttpException, + Logger } from '@nestjs/common'; import { FastifyReply, FastifyRequest } from 'fastify'; import { ApiErrorResponse } from 'picsur-shared/dist/dto/api/api.dto'; @@ -11,13 +12,26 @@ import { ApiErrorResponse } from 'picsur-shared/dist/dto/api/api.dto'; // (As long as its within nest, the earlier fastify stages are not handled here) // It neatly wraps the error for easier handling on the client -@Catch(HttpException) +@Catch() export class MainExceptionFilter implements ExceptionFilter { - catch(exception: HttpException, host: ArgumentsHost) { + private static readonly logger = new Logger('MainExceptionFilter'); + + catch(exception: unknown, host: ArgumentsHost) { + if (exception instanceof Error) { + MainExceptionFilter.logger.error(exception.message, exception.stack); + } else { + MainExceptionFilter.logger.error(exception); + } + const ctx = host.switchToHttp(); const response = ctx.getResponse(); const request = ctx.getRequest(); - const status = exception.getStatus(); + const status = + exception instanceof HttpException ? exception.getStatus() : 500; + const message = + exception instanceof HttpException + ? exception.message + : 'Internal server error'; const toSend: ApiErrorResponse = { success: false, @@ -25,7 +39,7 @@ export class MainExceptionFilter implements ExceptionFilter { timestamp: new Date().toISOString(), data: { - message: exception.message, + message, }, }; diff --git a/backend/src/managers/imagemanager/imagemanager.service.ts b/backend/src/managers/imagemanager/imagemanager.service.ts index 03a5d35..7c60d12 100644 --- a/backend/src/managers/imagemanager/imagemanager.service.ts +++ b/backend/src/managers/imagemanager/imagemanager.service.ts @@ -3,6 +3,7 @@ import { fileTypeFromBuffer, FileTypeResult } from 'file-type'; import { FullMime } from 'picsur-shared/dist/dto/mimes.dto'; import { AsyncFailable, HasFailed } from 'picsur-shared/dist/types'; import { ParseMime } from 'picsur-shared/dist/util/parse-mime'; +import { IsQOI } from 'qoi-img'; import { ImageDBService } from '../../collections/imagedb/imagedb.service'; import { EImageBackend } from '../../models/entities/image.entity'; import { ImageProcessorService } from './imageprocessor.service'; @@ -66,11 +67,18 @@ export class ImageManagerService { } private async getFullMimeFromBuffer(image: Buffer): AsyncFailable { - const mime: FileTypeResult | undefined = await fileTypeFromBuffer(image); + const filetypeResult: FileTypeResult | undefined = await fileTypeFromBuffer( + image, + ); - console.log(mime); + let mime: string | undefined; + if (filetypeResult === undefined) { + if (IsQOI(image)) mime = 'image/qoi'; + } else { + mime = filetypeResult.mime; + } - const fullMime = ParseMime(mime?.mime ?? 'extra/discard'); + const fullMime = ParseMime(mime ?? 'extra/discard'); return fullMime; } } diff --git a/backend/src/managers/imagemanager/imageprocessor.service.ts b/backend/src/managers/imagemanager/imageprocessor.service.ts index 47375de..a1bd836 100644 --- a/backend/src/managers/imagemanager/imageprocessor.service.ts +++ b/backend/src/managers/imagemanager/imageprocessor.service.ts @@ -7,7 +7,7 @@ import { SupportedMimeCategory } from 'picsur-shared/dist/dto/mimes.dto'; import { AsyncFailable, Fail } from 'picsur-shared/dist/types'; -import { QOIColorSpace, QOIencode } from 'qoi-img'; +import { QOIColorSpace, QOIdecode, QOIencode } from 'qoi-img'; import sharp from 'sharp'; import { UsrPreferenceService } from '../../collections/preferencesdb/usrpreferencedb.service'; @@ -50,10 +50,12 @@ export class ImageProcessorService { sharpImage = this.icoSharp(image); } else if (mime.mime === ImageMime.BMP) { sharpImage = this.bmpSharp(image); + } else if (mime.mime === ImageMime.QOI) { + sharpImage = this.qoiSharp(image); } else { sharpImage = sharp(image); } - mime.mime = ImageMime.PNG; + mime.mime = ImageMime.QOI; sharpImage = sharpImage.toColorspace('srgb'); @@ -111,4 +113,16 @@ export class ImageProcessorService { }, }); } + + private qoiSharp(image: Buffer) { + const result = QOIdecode(image); + + return sharp(result.pixels, { + raw: { + width: result.width, + height: result.height, + channels: result.channels, + }, + }); + } } diff --git a/frontend/src/app/components/picsur-img/picsur-img.component.ts b/frontend/src/app/components/picsur-img/picsur-img.component.ts index b4f0a84..bc2f78f 100644 --- a/frontend/src/app/components/picsur-img/picsur-img.component.ts +++ b/frontend/src/app/components/picsur-img/picsur-img.component.ts @@ -6,7 +6,9 @@ import { SimpleChanges, ViewChild } from '@angular/core'; -import { SupportedMimeCategory } from 'picsur-shared/dist/dto/mimes.dto'; +import { + SupportedMime +} from 'picsur-shared/dist/dto/mimes.dto'; import { HasFailed } from 'picsur-shared/dist/types'; import { URLRegex } from 'picsur-shared/dist/util/common-regex'; import { ParseMime } from 'picsur-shared/dist/util/parse-mime'; @@ -51,7 +53,7 @@ export class PicsurImgComponent implements OnChanges { return; } - if (mime.type === SupportedMimeCategory.Image) { + if (mime.mime === SupportedMime.QOI) { const result = await this.qoiWorker.decode(url); const canvas = this.canvas.nativeElement; @@ -60,10 +62,8 @@ export class PicsurImgComponent implements OnChanges { canvas.getContext('2d')?.putImageData(result.data, 0, 0); this.state = PicsurImgState.Canvas; - } else if (mime.type === SupportedMimeCategory.Animation) { - this.state = PicsurImgState.Image; } else { - this.logger.error(`Unsupported mime type: ${mime.type}`); + this.state = PicsurImgState.Image; } } diff --git a/shared/src/dto/mimes.dto.ts b/shared/src/dto/mimes.dto.ts index 9afd0a4..0358b91 100644 --- a/shared/src/dto/mimes.dto.ts +++ b/shared/src/dto/mimes.dto.ts @@ -6,6 +6,7 @@ export enum ImageMime { TIFF = 'image/tiff', BMP = 'image/bmp', ICO = 'image/x-icon', + QOI = 'image/qoi', } export enum AnimMime {