Skip to content

Commit

Permalink
Merge pull request #62 from Open-Webtoon-Reader/feature/websocket
Browse files Browse the repository at this point in the history
Merge download websocket to Main
  • Loading branch information
Xen0Xys authored Oct 26, 2024
2 parents 3c55e7d + 53e13b2 commit cbc80a7
Show file tree
Hide file tree
Showing 9 changed files with 5,668 additions and 269 deletions.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@
"@nestjs/config": "^3.2.2",
"@nestjs/core": "^10.3.10",
"@nestjs/platform-fastify": "^10.3.10",
"@nestjs/platform-socket.io": "^10.4.6",
"@nestjs/schedule": "^4.0.2",
"@nestjs/swagger": "^7.3.1",
"@nestjs/throttler": "^5.2.0",
"@nestjs/websockets": "^10.4.6",
"@prisma/client": "5.16.1",
"axios": "^1.7.2",
"class-transformer": "^0.5.1",
Expand All @@ -37,10 +39,12 @@
"jsdom": "^24.1.0",
"jszip": "^3.10.1",
"minio": "^8.0.1",
"nestjs-asyncapi": "^1.3.0",
"prisma": "^5.16.1",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"sharp": "^0.33.4",
"socket.io": "^4.8.1",
"swagger-themes": "^1.4.3"
},
"devDependencies": {
Expand Down
5,843 changes: 5,577 additions & 266 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {MigrationModule} from "./modules/webtoon/migration/migration.module";
import {ScheduleModule} from "@nestjs/schedule";
import {UpdateModule} from "./modules/webtoon/update/update.module";
import {ImageModule} from "./modules/webtoon/image/image.module";
import {WebsocketModule} from "./modules/websocket/websocket.module";

@Module({
imports: [
Expand All @@ -24,6 +25,7 @@ import {ImageModule} from "./modules/webtoon/image/image.module";
MigrationModule,
UpdateModule,
ImageModule,
WebsocketModule,
],
controllers: [],
providers: [
Expand Down
20 changes: 18 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {SwaggerTheme, SwaggerThemeNameEnum} from "swagger-themes";
import {LoggerMiddleware} from "./common/middlewares/logger.middleware";
import {Logger} from "@nestjs/common";
import {CustomValidationPipe} from "./common/pipes/custom-validation.pipe";
import {AsyncApiDocumentBuilder, AsyncApiModule} from "nestjs-asyncapi";

dotenv.config();

Expand Down Expand Up @@ -92,8 +93,8 @@ async function loadServer(server: NestFastifyApplication<RawServerDefault>, serv

// Swagger
const config = new DocumentBuilder()
.setTitle("Phoenix API")
.setDescription("Documentation for the Phoenix API")
.setTitle("OWR API")
.setDescription("Documentation for the OWR API")
.setVersion(process.env.npm_package_version)
.addBearerAuth()
.build();
Expand All @@ -112,6 +113,21 @@ async function loadServer(server: NestFastifyApplication<RawServerDefault>, serv
customCss,
});

// AsyncAPI
const asyncApiOptions = new AsyncApiDocumentBuilder()
.setTitle("OWR API")
.setDescription("Documentation for the OWR API")
.setVersion(process.env.npm_package_version)
.setDefaultContentType("application/json")
.addBearerAuth()
.addServer("owr-api", {
url: "http://localhost:4000",
protocol: "socket.io",
})
.build();
const asyncapiDocument = AsyncApiModule.createDocument(server, asyncApiOptions);
await AsyncApiModule.setup("async-api", server, asyncapiDocument);

server.useGlobalPipes(new CustomValidationPipe());
}

Expand Down
48 changes: 48 additions & 0 deletions src/modules/websocket/download.gateway.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {SubscribeMessage, WebSocketGateway, WebSocketServer} from "@nestjs/websockets";
import {Server} from "socket.io";
import {AsyncApiSub} from "nestjs-asyncapi";
import CachedWebtoonModel from "../webtoon/webtoon/models/models/cached-webtoon.model";

@WebSocketGateway({
namespace: "download",
cors: {
origin: "*",
}
})
export class DownloadGateway{

@WebSocketServer() socket: Server;

@SubscribeMessage("episode/progress")
@AsyncApiSub({
channel: "download/episode/progress",
message: {
payload: Number,
},
})
public onEpisodeProgress(progress: number){
this.socket.emit("episode/progress", progress);
}

@SubscribeMessage("progress")
@AsyncApiSub({
channel: "download/progress",
message: {
payload: Number,
},
})
public onDownloadProgress(progress: number){
this.socket.emit("progress", progress);
}

@SubscribeMessage("start")
@AsyncApiSub({
channel: "download/start",
message: {
payload: CachedWebtoonModel,
},
})
public onDownloadStart(webtoon: CachedWebtoonModel){
this.socket.emit("start", webtoon);
}
}
8 changes: 8 additions & 0 deletions src/modules/websocket/websocket.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import {Module} from "@nestjs/common";
import {DownloadGateway} from "./download.gateway";

@Module({
providers: [DownloadGateway],
exports: [DownloadGateway],
})
export class WebsocketModule{}
5 changes: 5 additions & 0 deletions src/modules/webtoon/webtoon/download-manager.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import WebtoonModel from "./models/models/webtoon.model";
import WebtoonDataModel from "./models/models/webtoon-data.model";
import {HttpStatusCode} from "axios";
import DownloadQueue from "../../../common/utils/models/download-queue";
import {DownloadGateway} from "../../websocket/download.gateway";

@Injectable()
export class DownloadManagerService{
Expand All @@ -23,6 +24,7 @@ export class DownloadManagerService{
private readonly webtoonParser: WebtoonParserService,
private readonly webtoonDatabase: WebtoonDatabaseService,
private readonly webtoonDownloader: WebtoonDownloaderService,
private readonly downloadGateway: DownloadGateway,
){
this.downloadQueue = DownloadQueue.loadQueue();
// If there are downloads in queue, start download
Expand Down Expand Up @@ -84,6 +86,7 @@ export class DownloadManagerService{
if(!currentDownload)
return;
this.logger.debug(`Downloading ${this.downloadQueue.getCurrentDownload().title} (${this.downloadQueue.getCurrentDownload().language}).`);
this.downloadGateway.onDownloadStart(currentDownload);
if(!await this.webtoonDatabase.isWebtoonSaved(this.downloadQueue.getCurrentDownload().title, this.downloadQueue.getCurrentDownload().language)){
const webtoon: WebtoonModel = await this.webtoonParser.getWebtoonInfos(this.downloadQueue.getCurrentDownload());
const webtoonData: WebtoonDataModel = await this.webtoonDownloader.downloadWebtoon(webtoon);
Expand All @@ -94,12 +97,14 @@ export class DownloadManagerService{
for(let i = startEpisode; i < epList.length; i++){
if(!this.downloadQueue.getCurrentDownload()) // If current download is cleared, stop downloading
break;
this.downloadGateway.onDownloadProgress(i / epList.length * 100);
const epImageLinks: string[] = await this.webtoonParser.getEpisodeLinks(this.downloadQueue.getCurrentDownload(), epList[i]);
const episodeData: EpisodeDataModel = await this.webtoonDownloader.downloadEpisode(epList[i], epImageLinks);
await this.webtoonDatabase.saveEpisode(currentDownload, epList[i], episodeData);
}
}
this.downloadQueue.clear();
this.downloadGateway.onDownloadStart(null);
}

getCurrentDownload(): CachedWebtoonModel{
Expand Down
4 changes: 4 additions & 0 deletions src/modules/webtoon/webtoon/webtoon-downloader.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import WebtoonDataModel from "./models/models/webtoon-data.model";
import WebtoonModel from "./models/models/webtoon.model";
import {Injectable, Logger} from "@nestjs/common";
import {MiscService} from "../../misc/misc.service";
import {DownloadGateway} from "../../websocket/download.gateway";

@Injectable()
export class WebtoonDownloaderService{
Expand All @@ -12,6 +13,7 @@ export class WebtoonDownloaderService{

constructor(
private readonly miscService: MiscService,
private readonly downloadGateway: DownloadGateway,
){}

async downloadEpisode(episode: EpisodeModel, imageUrls: string[]): Promise<EpisodeDataModel>{
Expand All @@ -25,6 +27,7 @@ export class WebtoonDownloaderService{
const elapsedSeconds = (Date.now() - startTime) / 1000;
const imagesPerSecond = downloadedCount / elapsedSeconds;
this.logger.debug(`Downloading ${downloadedCount} of ${imageUrls.length} images (${(imagesPerSecond).toFixed(2)} images/s)...`);
this.downloadGateway.onEpisodeProgress((downloadedCount / imageUrls.length) * 100);
}, 1000);

for (let i = 0; i < imageUrls.length; i++){
Expand All @@ -37,6 +40,7 @@ export class WebtoonDownloaderService{

clearInterval(interval);
this.logger.debug(`Downloaded ${downloadedCount}/${imageUrls.length} images in ${((Date.now() - startTime) / 1000).toFixed(2)} seconds.`);
this.downloadGateway.onEpisodeProgress(100);

// Convert all images to webp
const convertedImages: Buffer[] = await Promise.all(conversionPromises);
Expand Down
3 changes: 2 additions & 1 deletion src/modules/webtoon/webtoon/webtoon.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import {WebtoonDatabaseService} from "./webtoon-database.service";
import {DownloadManagerService} from "./download-manager.service";
import {MiscModule} from "../../misc/misc.module";
import {FileModule} from "../../file/file.module";
import {WebsocketModule} from "../../websocket/websocket.module";

@Module({
imports: [MiscModule, FileModule],
imports: [MiscModule, FileModule, WebsocketModule],
controllers: [WebtoonController],
providers: [WebtoonParserService, WebtoonDownloaderService, WebtoonDatabaseService, DownloadManagerService],
exports: [DownloadManagerService, WebtoonDatabaseService, WebtoonParserService, WebtoonDownloaderService],
Expand Down

0 comments on commit cbc80a7

Please sign in to comment.