diff --git a/backend/app.js b/backend/app.js index 5561adcb..b00b6987 100644 --- a/backend/app.js +++ b/backend/app.js @@ -255,9 +255,7 @@ app.post('/fileStatusMp4', function(req, res) { var fullpath = videoPath + name + ".mp4"; if (fs.existsSync(fullpath)) { exists = [basePath + videoPath + name, getFileSizeMp4(name)]; - } - else - { + } else { var percent = 0; var size = getFileSizeMp4(name); var downloaded = getAmountDownloadedMp4(name); @@ -386,6 +384,19 @@ app.post('/deleteMp4', function(req, res) { } }); +app.post('/downloadFile', function(req, res) { + let fileName = req.body.fileName; + let type = req.body.type; + let file = null; + if (type === 'audio') { + file = __dirname + '/' + 'audio/' + fileName + '.mp3'; + } else if (type === 'video') { + file = __dirname + '/' + 'video/' + fileName + '.mp4'; + } + + res.sendFile(file); +}); + app.get('/video/:id', function(req , res){ var head; diff --git a/backend/config/default.json b/backend/config/default.json index ac21d3b2..0538cb9a 100644 --- a/backend/config/default.json +++ b/backend/config/default.json @@ -16,7 +16,8 @@ }, "Extra": { "title_top": "Youtube Downloader", - "file_manager_enabled": true + "download_only_mode": false, + "file_manager_enabled": true } } } diff --git a/package.json b/package.json index b6acba82..8aef5878 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "@angular/platform-browser-dynamic": "^8.2.11", "@angular/router": "^8.2.11", "core-js": "^2.4.1", + "file-saver": "^2.0.2", "ng4-configure": "^0.1.7", "rxjs": "^6.5.3", "rxjs-compat": "^6.0.0-rc.0", @@ -32,10 +33,11 @@ "zone.js": "~0.9.1" }, "devDependencies": { - "@angular-devkit/build-angular": "~0.803.12", + "@angular-devkit/build-angular": "^0.803.24", "@angular/cli": "^8.3.12", "@angular/compiler-cli": "^8.2.11", "@angular/language-service": "^8.2.11", + "@types/file-saver": "^2.0.1", "@types/jasmine": "2.5.45", "@types/node": "~6.0.60", "codelyzer": "^5.0.1", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 3093217c..a025c2f0 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -5,6 +5,7 @@ import { Observable } from 'rxjs/Observable'; import {FormControl, Validators} from '@angular/forms'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {MatSnackBar} from '@angular/material'; +import { saveAs } from 'file-saver'; import 'rxjs/add/observable/of'; import 'rxjs/add/operator/mapTo'; import 'rxjs/add/operator/toPromise'; @@ -14,7 +15,9 @@ import 'rxjs/add/operator/toPromise'; templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) -export class AppComponent { +export class AppComponent implements OnInit { + iOS = false; + determinateProgress = false; downloadingfile = false; audioOnly: boolean; @@ -25,6 +28,7 @@ export class AppComponent { topBarTitle = 'Youtube Downloader'; percentDownloaded: number; fileManagerEnabled = false; + downloadOnlyMode = false; mp3s: any[] = []; mp4s: any[] = []; @@ -35,11 +39,12 @@ export class AppComponent { this.audioOnly = false; - + // loading config this.postsService.loadNavItems().subscribe(result => { // loads settings const backendUrl = result['YoutubeDLMaterial']['Host']['backendurl']; this.topBarTitle = result['YoutubeDLMaterial']['Extra']['title_top']; this.fileManagerEnabled = result['YoutubeDLMaterial']['Extra']['file_manager_enabled']; + this.downloadOnlyMode = result['YoutubeDLMaterial']['Extra']['download_only_mode']; this.postsService.path = backendUrl; this.postsService.startPath = backendUrl; @@ -54,30 +59,8 @@ export class AppComponent { }); } - /* - - doHandshake(url: string) { - this.postsService.startHandshake(url).subscribe(theurl => { - this.postsService.path = theurl; - this.postsService.handShakeComplete = true; - console.log('Handshake complete!'); - }, error => { - console.log('Initial handshake failed on http.'); - this.doHandshakeSSL(url); - }); - } - doHandshakeSSL(url: string) { - this.postsService.startHandshakeSSL(url).subscribe(theurl => { - this.postsService.path = theurl; - this.postsService.handShakeComplete = true; - console.log('Handshake complete!'); - }, - error => { - console.log('Initial handshake failed on https too! Make sure port 17442 is open.'); - this.postsService.handShakeComplete = false; - }); - }*/ + // file manager stuff getMp3s() { this.postsService.getMp3s().subscribe(result => { @@ -100,9 +83,9 @@ export class AppComponent { public goToFile(name, isAudio) { if (isAudio) { - this.downloadHelperMp3(name); + this.downloadHelperMp3(name, true); } else { - this.downloadHelperMp4(name); + this.downloadHelperMp4(name, true); } } @@ -124,10 +107,14 @@ export class AppComponent { } } + // app initialization. ngOnInit() { + this.iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window['MSStream']; } - downloadHelperMp3(name: string) { + // download helpers + + downloadHelperMp3(name: string, forceView = false) { this.postsService.getFileStatusMp3(name).subscribe(fileExists => { const exists = fileExists; this.exists = exists[0]; @@ -140,13 +127,25 @@ export class AppComponent { } setTimeout(() => this.downloadHelperMp3(name), 500); } else { - window.location.href = this.exists; + // if download only mode, just download the file. no redirect + if (forceView === false && this.downloadOnlyMode && !this.iOS) { + this.postsService.downloadFileFromServer(name, 'audio').subscribe(res => { + const blob: Blob = res; + saveAs(blob, name + '.mp3'); + this.downloadingfile = false; + }); + } else { + window.location.href = this.exists; + } + + // reloads mp3s + this.getMp3s(); } }); } - downloadHelperMp4(name: string) { + downloadHelperMp4(name: string, forceView = false) { this.postsService.getFileStatusMp4(name).subscribe(fileExists => { const exists = fileExists; this.exists = exists[0]; @@ -158,12 +157,25 @@ export class AppComponent { } setTimeout(() => this.downloadHelperMp4(name), 500); } else { - window.location.href = this.exists; + // if download only mode, just download the file. no redirect + if (forceView === false && this.downloadOnlyMode) { + this.postsService.downloadFileFromServer(name, 'video').subscribe(res => { + const blob: Blob = res; + saveAs(blob, name + '.mp4'); + this.downloadingfile = false; + }); + } else { + window.location.href = this.exists; + } + + // reloads mp4s + this.getMp4s(); } }); } + // download click handler downloadClicked() { if (this.ValidURL(this.url)) { this.urlError = false; @@ -197,6 +209,7 @@ export class AppComponent { } } + // checks if url is a valid URL ValidURL(str) { // tslint:disable-next-line: max-line-length const strRegex = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/; @@ -204,6 +217,7 @@ export class AppComponent { return re.test(str); } + // snackbar helper public openSnackBar(message: string, action: string) { this.snackBar.open(message, action, { duration: 2000, diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts index 45a8803c..32943cc1 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -1,5 +1,5 @@ import {Injectable, isDevMode} from '@angular/core'; -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpHeaders, HttpRequest, HttpResponseBase } from '@angular/common/http'; import config from '../assets/default.json'; import 'rxjs/add/operator/map'; import { Observable } from 'rxjs/Observable'; @@ -75,6 +75,10 @@ export class PostsService { getMp4s() { return this.http.post(this.path + 'getMp4s', {}); } + + downloadFileFromServer(fileName, type) { + return this.http.post(this.path + 'downloadFile', {fileName: fileName, type: type}, {responseType: 'blob'}); + } }