import { ForbiddenException, Injectable, NotFoundException, } from '@nestjs/common'; import { stat } from 'node:fs/promises'; import { PrismaService } from '../../infrastructure/database/prisma.service'; import { LocalFilesystemStorageService } from '../storage/storage.service'; export interface ArtworkDownload { filePath: string; contentLength: number; mimeType: string; } @Injectable() export class ArtworkService { constructor( private readonly prismaService: PrismaService, private readonly storageService: LocalFilesystemStorageService, ) {} async getOwnedArtworkDownload( artworkId: string, deviceId: string, ): Promise { const device = await this.prismaService.device.findUnique({ where: { id: deviceId }, select: { userId: true, }, }); if (!device) { throw new NotFoundException('Device not found'); } const artwork = await this.prismaService.artworkAsset.findUnique({ where: { id: artworkId }, select: { storageKey: true, mimeType: true, track: { select: { userId: true, }, }, }, }); if (!artwork || !artwork.track) { throw new NotFoundException('Artwork not found'); } if (artwork.track.userId !== device.userId) { throw new ForbiddenException('Artwork does not belong to this device user.'); } const filePath = this.storageService.resolve(artwork.storageKey); try { const fileStats = await stat(filePath); if (!fileStats.isFile()) { throw new NotFoundException('Artwork file not found'); } return { filePath, contentLength: fileStats.size, mimeType: artwork.mimeType, }; } catch (error) { if (error instanceof NotFoundException) { throw error; } throw new NotFoundException('Artwork file not found'); } } }