diff --git a/backend/src/images/image.entity.ts b/backend/src/images/image.entity.ts index 99ebb99..420eed2 100644 --- a/backend/src/images/image.entity.ts +++ b/backend/src/images/image.entity.ts @@ -13,6 +13,8 @@ export class Image { @ApiProperty() eventId: string; @ApiProperty() + lastModified: Date; + @ApiProperty() filename: string; @ApiProperty() title: string; diff --git a/backend/src/images/images.service.ts b/backend/src/images/images.service.ts index f2973ea..d2fd481 100644 --- a/backend/src/images/images.service.ts +++ b/backend/src/images/images.service.ts @@ -19,7 +19,14 @@ export class ImagesService { fileWatcher() { chokidar - .watch(`${baseFolder}/*/*.{jpeg,jpg}`, { ignoreInitial: true }) + .watch(`${baseFolder}/*/*.{jpeg,jpg}`, { + ignoreInitial: false, + ignored: (path) => { + return ( + path.endsWith('thumbnail.jpg') || path.endsWith('thumbnail.jpeg') + ); + }, + }) .on('all', async (fileevent, filepath) => { if (filepath.endsWith('.jpg') || filepath.endsWith('.jpeg')) { const folders = filepath.split(path.sep); @@ -28,15 +35,14 @@ export class ImagesService { const imageFile = folders[folders.length - 1]; switch (fileevent) { case 'add': - const image = await this.findOne(eventId, imageFile); const imageCreatedEvent: ImageCreatedEvent = { - image, + image: await this.findOne(eventId, imageFile), }; this.eventEmitter.emit('image.created', imageCreatedEvent); break; case 'change': const imageUpdatedEvent: ImageUpdatedEvent = { - image, + image: await this.findOne(eventId, imageFile), }; this.eventEmitter.emit('image.updated', imageUpdatedEvent); break; diff --git a/backend/src/images/iptc.parser.ts b/backend/src/images/iptc.parser.ts index c241786..c4cd73e 100644 --- a/backend/src/images/iptc.parser.ts +++ b/backend/src/images/iptc.parser.ts @@ -31,18 +31,20 @@ const iptcSchema = z.object({ export async function parseIptcFromFile(file: string): Promise { const data = fs.readFileSync(file); + const lastModified = fs.statSync(file).mtime; const iptc = await exifr.parse(data, { iptc: true }); const valitatedIptc = await iptcSchema.safeParseAsync(iptc); if (!valitatedIptc.success) { return null; } const parsedCaption = await parseIptcCaption(valitatedIptc.data.Caption); - const title = valitatedIptc.data.ObjectName; + const title = valitatedIptc.data.ObjectName.trim(); const timestamp = valitatedIptc.data.Headline; const windSpeed = getWindSpeedFromCaption(parsedCaption); const athletes = getAthletesFromCaption(parsedCaption); const event = getEventFromTitle(title); return { + lastModified, filename: path.parse(file).base, eventId: path.basename(path.dirname(file)), title, diff --git a/backend/src/live/live.gateway.ts b/backend/src/live/live.gateway.ts index d74075d..a8ac93d 100644 --- a/backend/src/live/live.gateway.ts +++ b/backend/src/live/live.gateway.ts @@ -5,7 +5,6 @@ import { OnGatewayConnection, } from '@nestjs/websockets'; import { Socket, Server } from 'socket.io'; -import { LiveService } from './live.service'; import { OnEvent } from '@nestjs/event-emitter'; import { ImageCreatedEvent } from 'src/images/events/image-created.event'; import { ImageDeletedEvent } from 'src/images/events/image-deleted.event'; @@ -20,8 +19,6 @@ export class LiveGateway implements OnGatewayConnection { @WebSocketServer() server: Server; - constructor(private readonly liveService: LiveService) {} - async handleConnection(client: Socket) { client.on('disconnecting', () => { client.rooms.forEach((room) => { diff --git a/backend/src/live/live.module.ts b/backend/src/live/live.module.ts index e7e145b..3577323 100644 --- a/backend/src/live/live.module.ts +++ b/backend/src/live/live.module.ts @@ -1,8 +1,7 @@ import { Module } from '@nestjs/common'; -import { LiveService } from './live.service'; import { LiveGateway } from './live.gateway'; @Module({ - providers: [LiveGateway, LiveService], + providers: [LiveGateway], }) export class LiveModule {} diff --git a/backend/src/live/live.service.ts b/backend/src/live/live.service.ts deleted file mode 100644 index bbb8274..0000000 --- a/backend/src/live/live.service.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class LiveService { - findAll() { - return `This action returns all live`; - } - - findOne(id: number) { - return `This action returns a #${id} live`; - } - - remove(id: number) { - return `This action removes a #${id} live`; - } -} diff --git a/frontend/src/pages/[event]/index.tsx b/frontend/src/pages/[event]/index.tsx index 897bdcb..e4d4dcc 100644 --- a/frontend/src/pages/[event]/index.tsx +++ b/frontend/src/pages/[event]/index.tsx @@ -37,11 +37,13 @@ export const Loader = async ({ params }: { params: { event: string } }) => { }; export default function EventPage() { - const { event, images, trackEvents } = useLoaderData() as { + const data = useLoaderData() as { event: Event; images: Image[]; trackEvents: string[]; }; + const { event, trackEvents } = data; + const [images, setImages] = useState(data.images); const eventId = useParams().event; const [watchers, setWatchers] = useState(1); const [trackEvent, setTrackEvent] = useState(""); @@ -56,9 +58,29 @@ export default function EventPage() { if (!watchers || isNaN(watchers)) return; setWatchers(watchers); }); + socket.on("image.created", (image: Image) => { + setImages((images) => [...images, image]); + }); + socket.on("image.updated", (image: Image) => { + setImages((images) => + images.map((i) => { + if (i.filename == image.filename) { + return image; + } + return i; + }) + ); + }); + + socket.on("image.deleted", (data: any) => { + const { filename } = data; + setImages((images) => images.filter((i) => i.filename != filename)); + }); return () => { if (!socket) return; - socket.off("imageChange"); + socket.off("image.created"); + socket.off("image.updated"); + socket.off("image.deleted"); socket.emit("leaveEvent", eventId); }; }, [socket, eventId]);