From 58c7842127e08281e2f4e44d70ff3ae6eb132fd1 Mon Sep 17 00:00:00 2001 From: Konstantin Date: Fri, 26 Apr 2024 17:23:45 +0200 Subject: [PATCH] added socket io for showing download progress on server & client --- client/package-lock.json | 45 +++++-- client/package.json | 1 + client/src/app/app.component.ts | 3 +- client/src/card/card.component.ts | 2 +- client/src/environment/environment.ts | 3 +- .../search-form/search-form.component.html | 9 +- .../src/search-form/search-form.component.ts | 11 +- client/src/services/socket.service.ts | 34 +++++ server/Dockerfile | 1 - server/package-lock.json | 122 +++++++++++++++++- server/package.json | 1 + server/src/index.ts | 6 +- server/src/middlewares/error-handler.ts | 2 +- server/src/routes/downloadRouter.ts | 41 ++++-- 14 files changed, 245 insertions(+), 36 deletions(-) create mode 100644 client/src/services/socket.service.ts diff --git a/client/package-lock.json b/client/package-lock.json index 7f9b1c7..ac58645 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -17,6 +17,7 @@ "@angular/platform-browser-dynamic": "^17.3.0", "@angular/router": "^17.3.0", "rxjs": "~7.8.0", + "socket.io-client": "^4.7.5", "tslib": "^2.3.0", "zone.js": "~0.14.3" }, @@ -3698,8 +3699,7 @@ "node_modules/@socket.io/component-emitter": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.1.tgz", - "integrity": "sha512-dzJtaDAAoXx4GCOJpbB2eG/Qj8VDpdwkLsWGzGm+0L7E8/434RyMbAHmk9ubXWVAb9nXmc44jUf8GKqVDiKezg==", - "dev": true + "integrity": "sha512-dzJtaDAAoXx4GCOJpbB2eG/Qj8VDpdwkLsWGzGm+0L7E8/434RyMbAHmk9ubXWVAb9nXmc44jUf8GKqVDiKezg==" }, "node_modules/@tufjs/canonical-json": { "version": "2.0.0", @@ -6090,7 +6090,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -6408,11 +6407,22 @@ "node": ">=10.2.0" } }, + "node_modules/engine.io-client": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", + "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, "node_modules/engine.io-parser": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", - "dev": true, "engines": { "node": ">=10.0.0" } @@ -10412,8 +10422,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -12932,11 +12941,24 @@ "ws": "~8.11.0" } }, + "node_modules/socket.io-client": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/socket.io-parser": { "version": "4.2.4", "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "dev": true, "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1" @@ -15383,7 +15405,6 @@ "version": "8.11.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", - "dev": true, "engines": { "node": ">=10.0.0" }, @@ -15400,6 +15421,14 @@ } } }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/client/package.json b/client/package.json index 9e89cb2..f38f688 100644 --- a/client/package.json +++ b/client/package.json @@ -20,6 +20,7 @@ "@angular/platform-browser-dynamic": "^17.3.0", "@angular/router": "^17.3.0", "rxjs": "~7.8.0", + "socket.io-client": "^4.7.5", "tslib": "^2.3.0", "zone.js": "~0.14.3" }, diff --git a/client/src/app/app.component.ts b/client/src/app/app.component.ts index 25dabe4..bdb5af6 100644 --- a/client/src/app/app.component.ts +++ b/client/src/app/app.component.ts @@ -3,6 +3,7 @@ import { DownloadService, MediaInfo } from '../services/download.service'; import { CardComponent } from '../card/card.component'; import { SearchFormComponent } from '../search-form/search-form.component'; import { CommonModule } from '@angular/common'; +import { ProgressData } from '../services/socket.service'; @Component({ selector: 'app-root', @@ -16,7 +17,7 @@ export class AppComponent implements OnInit { constructor(private downloadservice: DownloadService) {} - ngOnInit(): void { + ngOnInit() { this.downloadservice.loading$.subscribe((loading) => (this.loading = loading)); } diff --git a/client/src/card/card.component.ts b/client/src/card/card.component.ts index 57c17df..fbb7b1d 100644 --- a/client/src/card/card.component.ts +++ b/client/src/card/card.component.ts @@ -1,7 +1,7 @@ import { Component, Input } from '@angular/core'; import { DownloadService, MediaInfo, MediaType } from '../services/download.service'; -const errorMessage = 'Sorry, we could not download the requested file. Currently the maximum file size cannot exceed 4.5 Mb'; +const errorMessage = 'Sorry, we could not download the requested file.'; @Component({ selector: 'app-card', diff --git a/client/src/environment/environment.ts b/client/src/environment/environment.ts index 33e1b44..9037a5d 100644 --- a/client/src/environment/environment.ts +++ b/client/src/environment/environment.ts @@ -1,3 +1,4 @@ export const environment = { - serverUrl: 'https://0a40-5-147-251-186.ngrok-free.app', + // serverUrl: 'https://0a40-5-147-251-186.ngrok-free.app', + serverUrl: 'http://localhost:3000', }; diff --git a/client/src/search-form/search-form.component.html b/client/src/search-form/search-form.component.html index e6cc427..e2f7c1b 100644 --- a/client/src/search-form/search-form.component.html +++ b/client/src/search-form/search-form.component.html @@ -32,11 +32,12 @@ -
+
Loading... -

- Please be patient, the server may take up to two minutes to respond -

+
+

Downloading

+

{{ convertToPercentage(progressData) }}%

+
diff --git a/client/src/search-form/search-form.component.ts b/client/src/search-form/search-form.component.ts index b0d3fa5..0f6bdcc 100644 --- a/client/src/search-form/search-form.component.ts +++ b/client/src/search-form/search-form.component.ts @@ -4,6 +4,7 @@ import { FormsModule } from '@angular/forms'; import { CommonModule } from '@angular/common'; import { ErrorComponent } from '../error/error.component'; import { Subject, takeUntil } from 'rxjs'; +import { ProgressData, SocketService } from '../services/socket.service'; const errorMessage = 'Sorry, but the video could not be found.'; @@ -22,11 +23,15 @@ export class SearchFormComponent implements OnInit, OnDestroy { @Output() infoEmitter = new EventEmitter(); - constructor(private downloadService: DownloadService) {} + constructor( + private downloadService: DownloadService, + public socketService: SocketService, + ) {} ngOnInit() { this.downloadService.loading$.pipe(takeUntil(this.destroy$)).subscribe((loading) => (this.loading = loading)); this.downloadService.error$.pipe(takeUntil(this.destroy$)).subscribe((errorMessage) => (this.errorMessage = errorMessage)); + this.socketService.connect(); } ngOnDestroy() { @@ -48,4 +53,8 @@ export class SearchFormComponent implements OnInit, OnDestroy { }, }); } + + convertToPercentage(progress: ProgressData) { + return ((progress.totalDownloaded / progress.totalSize) * 100).toFixed(0); + } } diff --git a/client/src/services/socket.service.ts b/client/src/services/socket.service.ts new file mode 100644 index 0000000..64c18b6 --- /dev/null +++ b/client/src/services/socket.service.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@angular/core'; +import { Socket, io } from 'socket.io-client'; +import { environment } from '../environment/environment'; +import { Observable } from 'rxjs'; + +export interface ProgressData { + totalDownloaded: number; + totalSize: number; +} + +const downloadEvent = 'download-progress'; + +@Injectable({ providedIn: 'root' }) +export class SocketService { + private socket: Socket; + private progressData: Observable; + + constructor() { + this.socket = io(environment.serverUrl); + this.progressData = new Observable((subscriber) => { + this.socket.on(downloadEvent, (data: ProgressData) => { + subscriber.next(data); + }); + }); + } + + public connect() { + this.socket.connect(); + } + + public getProgressData() { + return this.progressData; + } +} diff --git a/server/Dockerfile b/server/Dockerfile index f5213d2..0018d28 100644 --- a/server/Dockerfile +++ b/server/Dockerfile @@ -7,5 +7,4 @@ COPY package.json package-lock.json ./ RUN npm install COPY . . -EXPOSE 80 CMD ["npm", "run", "start"] \ No newline at end of file diff --git a/server/package-lock.json b/server/package-lock.json index a6e3bc1..807120e 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -12,6 +12,7 @@ "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.19.2", + "socket.io": "^4.7.5", "ytdl-core": "^4.11.5" }, "devDependencies": { @@ -217,6 +218,11 @@ "node": ">= 8" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, "node_modules/@tsconfig/node10": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", @@ -260,11 +266,15 @@ "@types/node": "*" } }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" + }, "node_modules/@types/cors": { "version": "2.8.17", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -321,7 +331,6 @@ "version": "20.12.7", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", - "dev": true, "dependencies": { "undici-types": "~5.26.4" } @@ -856,6 +865,14 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -1186,7 +1203,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -1312,6 +1328,42 @@ "node": ">= 0.8" } }, + "node_modules/engine.io": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", + "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.2.tgz", + "integrity": "sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/es-abstract": { "version": "1.23.3", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.3.tgz", @@ -3059,8 +3111,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -3809,6 +3860,44 @@ "node": ">=8" } }, + "node_modules/socket.io": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.5.tgz", + "integrity": "sha512-DmeAkF6cwM9jSfmp6Dr/5/mfMwb5Z5qRrSXLpo3Fq5SqyU8CMF15jIN4ZhfSwu35ksM1qmHZDQ/DK5XTccSTvA==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.5.2", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz", + "integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -4414,8 +4503,7 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/unpipe": { "version": "1.0.0", @@ -4512,6 +4600,26 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/server/package.json b/server/package.json index 12f104f..e2975e2 100644 --- a/server/package.json +++ b/server/package.json @@ -35,6 +35,7 @@ "cors": "^2.8.5", "dotenv": "^16.4.5", "express": "^4.19.2", + "socket.io": "^4.7.5", "ytdl-core": "^4.11.5" } } diff --git a/server/src/index.ts b/server/src/index.ts index 7233d76..670724d 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -3,8 +3,12 @@ import express, { json } from 'express'; import { config } from '../config'; import { errorHandler } from './middlewares/error-handler'; import { downloadRouter } from './routes/downloadRouter'; +import { Server } from 'socket.io'; +import { createServer } from 'node:http'; const app = express(); +const server = createServer(app); +export const io = new Server(server, { cors: { origin: '*' } }); app.use(cors()); app.use(json()); @@ -12,5 +16,5 @@ app.use(json()); app.use(downloadRouter); const port = config.port; -app.listen(port, () => console.log(`Listening on ${port}`)); +server.listen(port, () => console.log(`Listening on ${port}`)); app.use(errorHandler); diff --git a/server/src/middlewares/error-handler.ts b/server/src/middlewares/error-handler.ts index 67311a9..58d6909 100644 --- a/server/src/middlewares/error-handler.ts +++ b/server/src/middlewares/error-handler.ts @@ -8,5 +8,5 @@ export const errorHandler: ErrorRequestHandler = (err, req, res, _next) => { return; } - res.status(500).json({ message: 'Internal server error' }); + return res.status(500).json({ message: 'Internal server error' }); }; diff --git a/server/src/routes/downloadRouter.ts b/server/src/routes/downloadRouter.ts index 45d5e1f..cfca8a5 100644 --- a/server/src/routes/downloadRouter.ts +++ b/server/src/routes/downloadRouter.ts @@ -1,9 +1,22 @@ import { Router } from 'express'; import { requestHandler } from '../error-handler'; import ytdl from 'ytdl-core'; +import { io } from '..'; export const downloadRouter = Router(); +interface ProgressData { + totalDownloaded: number; + totalSize: number; +} + +const downloadEvent = 'download-progress'; + +const onProgress = (_chunkLen: number, totalDownloaded: number, totalSize: number) => { + const data: ProgressData = { totalDownloaded, totalSize }; + io.emit(downloadEvent, data); +}; + downloadRouter.get( '/info', requestHandler(async (req, res) => { @@ -29,14 +42,18 @@ downloadRouter.get( requestHandler(async (req, res) => { const url = req.query.url as string; + const stream = ytdl(url, { filter: 'audioandvideo', quality: 'highestvideo' }); + + stream.on('progress', onProgress); + + stream.on('error', () => { + throw new Error('Couldnot load audio stream'); + }); + res.header('Content-Disposition', `attachment; filename="video.mp4"`); res.header('Content-Type', 'video/mp4'); - ytdl(url, { filter: 'audioandvideo', quality: 'highestvideo' }) - .pipe(res) - .on('error', () => { - throw new Error('Could not load video stream'); - }); + stream.pipe(res); }), ); @@ -45,13 +62,17 @@ downloadRouter.get( requestHandler(async (req, res) => { const url = req.query.url as string; + const stream = ytdl(url, { filter: 'audioonly' }); + + stream.on('progress', onProgress); + + stream.on('error', () => { + throw new Error('Could not load audio stream'); + }); + res.header('Content-Disposition', 'attachment; filename="audio.webm"'); res.header('Content-Type', 'audio/webm'); - ytdl(url, { filter: 'audioonly' }) - .pipe(res) - .on('error', () => { - throw new Error('Could not load audio stream'); - }); + stream.pipe(res); }), );