import {Component, ElementRef, ViewChild} from '@angular/core'; import axios from "axios"; import {DevelopmentStore} from "../../store/DevelopmentStore"; import {FormsModule} from "@angular/forms"; import {DecimalPipe, NgClass, NgIf} from "@angular/common"; import funfacts from "../../assets/funfacts"; import {ActivatedRoute, RouterLink} from "@angular/router"; @Component({ selector: 'app-download', standalone: true, imports: [ FormsModule, NgIf, DecimalPipe, RouterLink, NgClass ], templateUrl: './download.component.html', styleUrl: './download.component.scss' }) export class DownloadComponent { @ViewChild('download_not_possible') download_not_possible: ElementRef | undefined; @ViewChild('file_not_found_modal') file_not_found_modal: ElementRef | undefined; inputFileId: string = ""; fileId: string = ""; filePassword: string = ""; fileName: string = ""; downloadInfo: DownloadInfo | null = null; fileDownloadStarted: boolean = false; fileDownloadFinished: boolean = false; waitingForPassword: boolean = false; downloadProgress: number = 0; targetUploadProgress: number = 0; downloadDuration: string = ""; passwordWrong: boolean = false; funfact: string = ""; constructor(private developmentStore: DevelopmentStore, private activatedRoute: ActivatedRoute) { this.funfact = funfacts[Math.floor(Math.random() * funfacts.length)]; this.activatedRoute.queryParams.subscribe(params => { const fileId = params['fileId']; const password = params['password']; if(password) { this.filePassword = password; } if(fileId) { this.inputFileId = fileId; this.requestDownload(); } }); this.speedTest(); } requestDownload() { if(this.waitingForPassword) { console.log("Requesting download with password"); this.downloadFile(); return; } console.log(this.inputFileId); this.fileId = this.inputFileId; // TODO: Implement link extraction logic this.getDownloadInfo(); } processDownloadInfo() { if(!this.downloadInfo?.passwordProtected && this.downloadInfo?.downloadable) { console.log("Proceeding with download"); this.downloadFile(); return; } else if(!this.downloadInfo?.downloadable) { this.download_not_possible?.nativeElement.showModal(); return; } else if(this.downloadInfo?.passwordProtected) { if(this.filePassword) { console.log("Requesting download with password"); this.downloadFile(); return; } this.waitingForPassword = true; console.log("Password protected"); } } private speedTest() { const start = new Date().getTime(); // Start timer axios({ method: 'get', url: this.developmentStore.getBaseUrl() + 'api/v1/speed-test', responseType: 'arraybuffer', }) .then(response => { const end = new Date().getTime(); // End timer const duration = (end - start) / 1000; // Convert ms to seconds const bitsLoaded = response.data.byteLength * 8; const speedBps = bitsLoaded / duration; const speedKbps = speedBps / 1024; const speedMbps = speedKbps / 1024; console.log(`Download speed: ${speedMbps.toFixed(2)} Mbps`); }) .catch(error => { console.error('Error during download test:', error); }); } private getDownloadInfo() { axios({ method: 'get', url: this.developmentStore.getBaseUrl() + 'api/v1/download-info?fileId=' + this.fileId, responseType: 'json', }) .then(response => { this.downloadInfo = response.data; console.log(response.data); this.processDownloadInfo(); }) .catch(error => { this.file_not_found_modal?.nativeElement.showModal(); console.error('Error during download info request:', error); }); } private downloadFile() { const startTime = new Date().getTime(); this.fileDownloadStarted = true; axios({ method: 'get', url: this.developmentStore.getBaseUrl() + 'api/v1/download?fileId=' + this.fileId + '&password=' + this.filePassword, responseType: 'arraybuffer', onDownloadProgress: (progressEvent) => { const endTime = new Date().getTime(); const duration = (endTime - startTime) / 1000; this.downloadDuration = duration.toFixed(0); // Calculate the percentage of download completed if(progressEvent.total) { const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total); this.targetUploadProgress = percentCompleted; this.smoothProgressUpdate(); } } }) .then(response => { const endTime = new Date().getTime(); const duration = (endTime - startTime) / 1000; this.downloadDuration = duration.toFixed(2); this.fileDownloadFinished = true; const contentDisposition = response.headers['content-disposition']; let filename = "default_filename"; // Default filename in case parsing fails if (contentDisposition) { const filenameRegex = /filename="?([^"]+)"?/; const matches = contentDisposition.match(filenameRegex); if (matches && matches[1]) { filename = matches[1]; } } this.fileName = filename; const blob = new Blob([response.data], {type: 'application/octet-stream'}); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.style.display = 'none'; a.href = url; a.download = filename; // You can specify a filename here document.body.appendChild(a); a.click(); // Clean up by revoking the Blob URL and removing the temporary anchor window.URL.revokeObjectURL(url); document.body.removeChild(a); }) .catch(error => { this.fileDownloadStarted = false; this.wrongPassword(); console.error('Error during download request:', error); }); } private wrongPassword() { this.passwordWrong = true; } smoothProgressUpdate() { if (this.downloadProgress < this.targetUploadProgress) { this.downloadProgress += 0.01 * (this.targetUploadProgress - this.downloadProgress); requestAnimationFrame(this.smoothProgressUpdate.bind(this)); } else if (this.downloadProgress > this.targetUploadProgress) { // Handle overshoot this.downloadProgress = this.targetUploadProgress; } } } interface DownloadInfo { downloadable: boolean; passwordProtected: boolean; singleDownload: boolean; fileId: string; }