From a0067e2e1ccb153ea91937e927b315be5b9a14f3 Mon Sep 17 00:00:00 2001 From: Xen0Xys Date: Sat, 18 May 2024 14:33:49 +0200 Subject: [PATCH] :sparkles: Implement chunk loading for episodes (limit to 25 per chunks) --- .../webtoon/models/dto/chunk-number.dto.ts | 9 ++++++++ .../responses/episode-chunk.response.ts | 21 +++++++++++++++++++ .../webtoon/webtoon-database.service.ts | 20 +++++++++++++----- .../webtoon/webtoon/webtoon.controller.ts | 12 ++++++----- 4 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 src/modules/webtoon/webtoon/models/dto/chunk-number.dto.ts create mode 100644 src/modules/webtoon/webtoon/models/responses/episode-chunk.response.ts diff --git a/src/modules/webtoon/webtoon/models/dto/chunk-number.dto.ts b/src/modules/webtoon/webtoon/models/dto/chunk-number.dto.ts new file mode 100644 index 0000000..9f5fc1f --- /dev/null +++ b/src/modules/webtoon/webtoon/models/dto/chunk-number.dto.ts @@ -0,0 +1,9 @@ +import {ApiProperty} from "@nestjs/swagger"; +import {IsInt, IsOptional} from "class-validator"; + +export class ChunkNumberDto{ + @ApiProperty({required: false}) + @IsOptional() + @IsInt() + chunk: number; +} diff --git a/src/modules/webtoon/webtoon/models/responses/episode-chunk.response.ts b/src/modules/webtoon/webtoon/models/responses/episode-chunk.response.ts new file mode 100644 index 0000000..5014c0d --- /dev/null +++ b/src/modules/webtoon/webtoon/models/responses/episode-chunk.response.ts @@ -0,0 +1,21 @@ +import EpisodeLineModel from "../models/episode-line.model"; +import {ApiProperty} from "@nestjs/swagger"; + +export default class EpisodeChunkResponse{ + @ApiProperty() + episodes: EpisodeLineModel[]; + @ApiProperty() + currentChunk: number; + @ApiProperty() + maxChunks: number; + + constructor( + episodes: EpisodeLineModel[], + currentChunk: number, + maxChunks: number + ){ + this.episodes = episodes; + this.currentChunk = currentChunk; + this.maxChunks = maxChunks; + } +} diff --git a/src/modules/webtoon/webtoon/webtoon-database.service.ts b/src/modules/webtoon/webtoon/webtoon-database.service.ts index 6329815..5881fee 100644 --- a/src/modules/webtoon/webtoon/webtoon-database.service.ts +++ b/src/modules/webtoon/webtoon/webtoon-database.service.ts @@ -12,10 +12,13 @@ import {PrismaService} from "../../misc/prisma.service"; import {MiscService} from "../../misc/misc.service"; import ImageTypes from "./models/enums/image-types"; import WebtoonResponse from "./models/responses/webtoon-response"; +import EpisodeChunkResponse from "./models/responses/episode-chunk.response"; @Injectable() export class WebtoonDatabaseService{ + private readonly CHUNK_SIZE: number = 25; + constructor( private readonly prismaService: PrismaService, private readonly miscService: MiscService @@ -267,7 +270,7 @@ export class WebtoonDatabaseService{ ); } - async getEpisodes(webtoonId: number): Promise{ + async getEpisodes(webtoonId: number, chunkNumber: number): Promise{ const dbWebtoon: any = await this.prismaService.webtoons.findFirst({ where: { id: webtoonId @@ -275,6 +278,11 @@ export class WebtoonDatabaseService{ }); if(!dbWebtoon) throw new NotFoundException(`Webtoon with id ${webtoonId} not found in database.`); + const episodeCount: number = await this.prismaService.episodes.count({ + where: { + webtoon_id: webtoonId + } + }); const episodes: any[] = await this.prismaService.episodes.findMany({ where: { webtoon_id: webtoonId @@ -284,14 +292,16 @@ export class WebtoonDatabaseService{ }, orderBy: { number: "asc" - } + }, + skip: (chunkNumber - 1) * this.CHUNK_SIZE, + take: this.CHUNK_SIZE }); - const response: EpisodeLineModel[] = []; + const episodeLines: EpisodeLineModel[] = []; for(const episode of episodes){ const thumbnail: string = this.miscService.bufferToDataURL(this.loadImage(episode.thumbnail.sum)); - response.push(new EpisodeLineModel(episode.id, episode.title, episode.number, thumbnail)); + episodeLines.push(new EpisodeLineModel(episode.id, episode.title, episode.number, thumbnail)); } - return response; + return new EpisodeChunkResponse(episodeLines, chunkNumber, Math.ceil(episodeCount / this.CHUNK_SIZE)); } async getEpisodeInfos(episodeId: number): Promise{ diff --git a/src/modules/webtoon/webtoon/webtoon.controller.ts b/src/modules/webtoon/webtoon/webtoon.controller.ts index c85f437..f479514 100644 --- a/src/modules/webtoon/webtoon/webtoon.controller.ts +++ b/src/modules/webtoon/webtoon/webtoon.controller.ts @@ -1,4 +1,4 @@ -import {Controller, Get, Param} from "@nestjs/common"; +import {Controller, Get, Param, Query} from "@nestjs/common"; import {ApiResponse, ApiTags} from "@nestjs/swagger"; import {WebtoonDatabaseService} from "./webtoon-database.service"; import {WebtoonIdDto} from "./models/dto/webtoon-id.dto"; @@ -7,7 +7,8 @@ import EpisodeResponse from "./models/responses/episode.response"; import LightWebtoonResponse from "./models/responses/light-webtoon-response"; import {Throttle} from "@nestjs/throttler"; import WebtoonResponse from "./models/responses/webtoon-response"; -import EpisodeLineModel from "./models/models/episode-line.model"; +import EpisodeChunkResponse from "./models/responses/episode-chunk.response"; +import {ChunkNumberDto} from "./models/dto/chunk-number.dto"; @Controller("webtoons") @ApiTags("Webtoon") @@ -32,10 +33,11 @@ export class WebtoonController{ } @Get(":webtoonId/episodes") - @ApiResponse({status: 200, description: "Returns a list of episodes for a webtoon", type: EpisodeLineModel, isArray: true}) + @ApiResponse({status: 200, description: "Returns a list of episodes for a webtoon", type: EpisodeChunkResponse}) @ApiResponse({status: 404, description: "Webtoon not found"}) - async getWebtoonEpisodes(@Param() webtoonIdDto: WebtoonIdDto): Promise{ - return this.webtoonDatabaseService.getEpisodes(webtoonIdDto.webtoonId); + async getWebtoonEpisodes(@Param() webtoonIdDto: WebtoonIdDto, @Query() chunkNumberDto: ChunkNumberDto): Promise{ + const chunk = chunkNumberDto.chunk ?? 1; + return this.webtoonDatabaseService.getEpisodes(webtoonIdDto.webtoonId, chunk); } @Get("episodes/:episodeId")