sharepulse/frontend/src/app/download/download.component.ts
2024-02-25 00:17:18 +01:00

213 lines
6.5 KiB
TypeScript

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<HTMLDialogElement> | undefined;
@ViewChild('file_not_found_modal') file_not_found_modal: ElementRef<HTMLDialogElement> | 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;
}