diff --git a/backend/app.js b/backend/app.js index 6c378e3..5ba74a3 100644 --- a/backend/app.js +++ b/backend/app.js @@ -48,6 +48,125 @@ app.get('/using-encryption', function(req, res) { res.end("yes"); }); +// objects + +function File(id, title, thumbnailURL, isAudio, duration) { + this.id = id; + this.title = title; + this.thumbnailURL = thumbnailURL; + this.isAudio = isAudio; + this.duration = duration; +} + +// actual functions + +function getThumbnailMp3(name) +{ + var obj = getJSONMp3(name); + var thumbnailLink = obj.thumbnail; + return thumbnailLink; +} + +function getThumbnailMp4(name) +{ + var obj = getJSONMp4(name); + var thumbnailLink = obj.thumbnail; + return thumbnailLink; +} + +function getFileSizeMp3(name) +{ + var jsonPath = audioPath+name+".mp3.info.json"; + + if (fs.existsSync(jsonPath)) + var obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8')); + else + var obj = 0; + + return obj.filesize; +} + +function getFileSizeMp4(name) +{ + var jsonPath = videoPath+name+".info.json"; + var filesize = 0; + if (fs.existsSync(jsonPath)) + { + var obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8')); + var format = obj.format.substring(0,3); + for (i = 0; i < obj.formats.length; i++) + { + if (obj.formats[i].format_id == format) + { + filesize = obj.formats[i].filesize; + } + } + } + + return filesize; +} + +function getJSONMp3(name) +{ + var jsonPath = audioPath+name+".mp3.info.json"; + if (fs.existsSync(jsonPath)) + var obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8')); + else + var obj = 0; + + return obj; +} + +function getJSONMp4(name) +{ + var jsonPath = videoPath+name+".info.json"; + if (fs.existsSync(jsonPath)) + { + var obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8')); + return obj; + } + else return 0; +} + +function getAmountDownloadedMp3(name) +{ + var partPath = audioPath+name+".mp3.part"; + if (fs.existsSync(partPath)) + { + const stats = fs.statSync(partPath); + const fileSizeInBytes = stats.size; + return fileSizeInBytes; + } + else + return 0; +} + + + +function getAmountDownloadedMp4(name) +{ + var format = getVideoFormatID(name); + var partPath = videoPath+name+".f"+format+".mp4.part"; + if (fs.existsSync(partPath)) + { + const stats = fs.statSync(partPath); + const fileSizeInBytes = stats.size; + return fileSizeInBytes; + } + else + return 0; +} + +function getVideoFormatID(name) +{ + var jsonPath = videoPath+name+".info.json"; + if (fs.existsSync(jsonPath)) + { + var obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8')); + var format = obj.format.substring(0,3); + return format; + } +} app.post('/tomp3', function(req, res) { var url = req.body.url; @@ -81,76 +200,6 @@ app.post('/tomp3', function(req, res) { res.end("yes"); }); -function getFileSizeMp3(name) -{ - var jsonPath = audioPath+name+".mp3.info.json"; - - if (fs.existsSync(jsonPath)) - var obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8')); - else - var obj = 0; - - return obj.filesize; -} - -function getAmountDownloadedMp3(name) -{ - var partPath = audioPath+name+".mp3.part"; - if (fs.existsSync(partPath)) - { - const stats = fs.statSync(partPath); - const fileSizeInBytes = stats.size; - return fileSizeInBytes; - } - else - return 0; -} - -function getFileSizeMp4(name) -{ - var jsonPath = videoPath+name+".info.json"; - var filesize = 0; - if (fs.existsSync(jsonPath)) - { - var obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8')); - var format = obj.format.substring(0,3); - for (i = 0; i < obj.formats.length; i++) - { - if (obj.formats[i].format_id == format) - { - filesize = obj.formats[i].filesize; - } - } - } - - return filesize; -} - -function getAmountDownloadedMp4(name) -{ - var format = getVideoFormatID(name); - var partPath = videoPath+name+".f"+format+".mp4.part"; - if (fs.existsSync(partPath)) - { - const stats = fs.statSync(partPath); - const fileSizeInBytes = stats.size; - return fileSizeInBytes; - } - else - return 0; -} - -function getVideoFormatID(name) -{ - var jsonPath = videoPath+name+".info.json"; - if (fs.existsSync(jsonPath)) - { - var obj = JSON.parse(fs.readFileSync(jsonPath, 'utf8')); - var format = obj.format.substring(0,3); - return format; - } -} - app.post('/tomp4', function(req, res) { var url = req.body.url; var date = Date.now(); @@ -168,7 +217,8 @@ app.post('/tomp4', function(req, res) { res.end("yes"); }); -app.post('/mp3fileexists', function(req, res) { +// gets the status of the mp3 file that's being downloaded +app.post('/fileStatusMp3', function(req, res) { var name = req.body.name + ""; var exists = ""; var fullpath = audioPath + name + ".mp3"; @@ -189,7 +239,8 @@ app.post('/mp3fileexists', function(req, res) { res.end("yes"); }); -app.post('/mp4fileexists', function(req, res) { +// gets the status of the mp4 file that's being downloaded +app.post('/fileStatusMp4', function(req, res) { var name = req.body.name; var exists = ""; var fullpath = videoPath + name + ".mp4"; @@ -210,6 +261,113 @@ app.post('/mp4fileexists', function(req, res) { res.end("yes"); }); +// gets all download mp3s +app.post('/getMp3s', function(req, res) { + var mp3s = []; + var fullpath = audioPath; + var files = fs.readdirSync(audioPath); + + for (var i in files) + { + var nameLength = path.basename(files[i]).length; + var ext = path.basename(files[i]).substring(nameLength-4, nameLength); + if (ext == ".mp3") + { + var jsonobj = getJSONMp3(path.basename(files[i]).substring(0, path.basename(files[i]).length-4)); + var id = path.basename(files[i]).substring(0, path.basename(files[i]).length-4); + var title = jsonobj.title; + + if (title.length > 14) // edits title if it's too long + { + title = title.substring(0,12) + "..."; + } + + var thumbnail = jsonobj.thumbnail; + var duration = jsonobj.duration; + var isaudio = true; + var file = new File(id, title, thumbnail, isaudio, duration); + mp3s.push(file); + } + } + + res.send(mp3s); + res.end("yes"); +}); + +// gets all download mp4s +app.post('/getMp4s', function(req, res) { + var mp4s = []; + var fullpath = videoPath; + var files = fs.readdirSync(videoPath); + + for (var i in files) + { + var nameLength = path.basename(files[i]).length; + var ext = path.basename(files[i]).substring(nameLength-4, nameLength); + if (ext == ".mp4") + { + var jsonobj = getJSONMp4(path.basename(files[i]).substring(0, path.basename(files[i]).length-4)); + var id = path.basename(files[i]).substring(0, path.basename(files[i]).length-4); + var title = jsonobj.title; + + if (title.length > 14) // edits title if it's too long + { + title = title.substring(0,12) + "..."; + } + + var thumbnail = jsonobj.thumbnail; + var duration = jsonobj.duration; + var isaudio = false; + var file = new File(id, title, thumbnail, isaudio, duration); + mp4s.push(file); + } + } + + res.send(mp4s); + res.end("yes"); +}); + +// deletes mp3 file +app.post('/deleteMp3', function(req, res) { + var name = req.body.name; + var fullpath = audioPath + name + ".mp3"; + var wasDeleted = false; + if (fs.existsSync(fullpath)) + { + fs.unlink(fullpath); + wasDeleted = true; + res.send(wasDeleted); + res.end("yes"); + } + else + { + wasDeleted = false; + res.send(wasDeleted); + res.end("yes"); + } +}); + +// deletes mp4 file +app.post('/deleteMp4', function(req, res) { + var name = req.body.name; + var fullpath = videoPath + name + ".mp4"; + var wasDeleted = false; + if (fs.existsSync(fullpath)) + { + fs.unlink(fullpath); + wasDeleted = true; + res.send(wasDeleted); + res.end("yes"); + } + else + { + wasDeleted = false; + res.send(wasDeleted); + res.end("yes"); + } +}); + + app.get('/video/:id', function(req , res){ var head; const path = "video/" + req.params.id + ".mp4"; diff --git a/src/app/app.component.html b/src/app/app.component.html index 1ede66b..6696f72 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,14 +1,14 @@ - - - - -
- -
{{topBarTitle}}
-
- -
+ + + + +
+ +
{{topBarTitle}}
+
+ +

@@ -27,19 +27,66 @@
Only Audio - +
-
- -
- - - +
+ +
+ + + +
+ + + + + Audio + + + Your audio files are here + + +
+ + + + + +
+ +
+ + + + Video + + + Your video files are here + + +
+ + + + + +
+
+
+ + + + + + + + \ No newline at end of file diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 8b7413e..2dbc0eb 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,5 +1,6 @@ import { Component, OnInit } from '@angular/core'; import {PostsService} from './posts.services'; +import {FileCardComponent} from './file-card/file-card.component'; import { Observable } from 'rxjs/Observable'; import {FormControl, Validators} from '@angular/forms'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; @@ -23,6 +24,10 @@ export class AppComponent { exists: string = ""; topBarTitle: string = "Youtube Downloader"; percentDownloaded: number; + + mp3s: any[] = []; + mp4s: any[] = []; + constructor(private postsService: PostsService, public snackBar: MatSnackBar) { this.audioOnly = true; @@ -35,10 +40,14 @@ export class AppComponent { this.postsService.path = backendUrl; this.postsService.startPath = backendUrl; this.postsService.startPathSSL = backendUrl; + + this.getMp3s(); + this.getMp4s(); }, error => { console.log(error); }); + } urlForm = new FormControl('', [Validators.required]); @@ -67,6 +76,48 @@ export class AppComponent { }); } + getMp3s() { + this.postsService.getMp3s().subscribe(result => { + var mp3s = result; + this.mp3s = mp3s; + }, + error => { + console.log(error); + }); + } + + getMp4s() { + this.postsService.getMp4s().subscribe(result => { + var mp4s = result; + this.mp4s = mp4s; + }, + error => { + console.log(error); + }); + } + + public removeFromMp3(name: string) + { + for (var i = 0; i < this.mp3s.length; i++) + { + if (this.mp3s[i].id == name) + { + this.mp3s.splice(i,1); + } + } + } + + public removeFromMp4(name: string) + { + for (var i = 0; i < this.mp4s.length; i++) + { + if (this.mp4s[i].id == name) + { + this.mp4s.splice(i,1); + } + } + } + ngOnInit() { } @@ -167,7 +218,7 @@ export class AppComponent { return re.test(str); } - openSnackBar(message: string, action: string) { + public openSnackBar(message: string, action: string) { this.snackBar.open(message, action, { duration: 2000, }); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index cdfeed1..8056bc3 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,8 +1,9 @@ import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import {MatNativeDateModule, MatRadioModule, MatInputModule, MatButtonModule, MatSidenavModule, MatIconModule, MatListModule, - MatSnackBarModule, MatCardModule, MatSelectModule, MatToolbarModule, MatCheckboxModule, - MatProgressBarModule } from '@angular/material'; + MatSnackBarModule, MatCardModule, MatSelectModule, MatToolbarModule, MatCheckboxModule, MatGridListModule, + MatProgressBarModule, MatExpansionModule, + MatGridList} from '@angular/material'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import { AppComponent } from './app.component'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; @@ -10,10 +11,12 @@ import { HttpModule } from '@angular/http'; import { HttpClientModule } from '@angular/common/http'; import { PostsService } from 'app/posts.services'; import {APP_BASE_HREF} from '@angular/common'; +import { FileCardComponent } from './file-card/file-card.component'; @NgModule({ declarations: [ - AppComponent + AppComponent, + FileCardComponent ], imports: [ BrowserModule, @@ -34,6 +37,8 @@ import {APP_BASE_HREF} from '@angular/common'; MatSidenavModule, MatIconModule, MatListModule, + MatGridListModule, + MatExpansionModule, MatProgressBarModule ], providers: [PostsService], diff --git a/src/app/file-card/file-card.component.css b/src/app/file-card/file-card.component.css new file mode 100644 index 0000000..87e1049 --- /dev/null +++ b/src/app/file-card/file-card.component.css @@ -0,0 +1,33 @@ +.example-card { + width: 150px; + height: 125px; + padding: 0px; +} + +.deleteButton { + top:-5px; + right:-5px; + position:absolute; +} + +/* Coerce the icon container away from display:inline */ +.mat-icon-button .mat-button-wrapper { + display: flex; + justify-content: center; +} + +.image { + max-width:100%; + max-height:100%; +} + +.example-full-width-height { + width: 100%; + height: 100% +} + +.centered { + margin: 0 auto; + top: 50%; + left: 50%; +} \ No newline at end of file diff --git a/src/app/file-card/file-card.component.html b/src/app/file-card/file-card.component.html new file mode 100644 index 0000000..4579676 --- /dev/null +++ b/src/app/file-card/file-card.component.html @@ -0,0 +1,13 @@ + + +
+ {{title}} +
+ ID: {{name}} +
+ + + +
Thumbnail
+ +
diff --git a/src/app/file-card/file-card.component.spec.ts b/src/app/file-card/file-card.component.spec.ts new file mode 100644 index 0000000..722eee2 --- /dev/null +++ b/src/app/file-card/file-card.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { FileCardComponent } from './file-card.component'; + +describe('FileCardComponent', () => { + let component: FileCardComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ FileCardComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(FileCardComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/file-card/file-card.component.ts b/src/app/file-card/file-card.component.ts new file mode 100644 index 0000000..c0b6c51 --- /dev/null +++ b/src/app/file-card/file-card.component.ts @@ -0,0 +1,48 @@ +import { Component, OnInit, Input } from '@angular/core'; +import {PostsService} from '../posts.services'; +import {MatSnackBar} from '@angular/material'; +import {AppComponent} from '../app.component'; + +@Component({ + selector: 'app-file-card', + templateUrl: './file-card.component.html', + styleUrls: ['./file-card.component.css'] +}) +export class FileCardComponent implements OnInit { + + @Input() title:string; + @Input() length:string; + @Input() name:string; + @Input() thumbnailURL: string; + @Input() isAudio: boolean = true; + + constructor(private postsService: PostsService, public snackBar: MatSnackBar, private appComponent: AppComponent) { } + + ngOnInit() { + } + + deleteFile() + { + this.postsService.deleteFile(this.name, this.isAudio).subscribe(result => { + if (result == true) + { + this.openSnackBar("Delete success!", "OK."); + if (this.isAudio) + this.appComponent.removeFromMp3(name); + else + this.appComponent.removeFromMp4(name); + } + else + { + this.openSnackBar("Delete failed!", "OK."); + } + }); + } + + 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 5f31bf5..b1da4fd 100644 --- a/src/app/posts.services.ts +++ b/src/app/posts.services.ts @@ -56,12 +56,12 @@ export class PostsService { } getFileStatusMp3(name: string): Observable { - return this.http.post(this.path + "mp3fileexists",{name: name}) + return this.http.post(this.path + "fileStatusMp3",{name: name}) .map(res => res.json()); } getFileStatusMp4(name: string): Observable { - return this.http.post(this.path + "mp4fileexists",{name: name}) + return this.http.post(this.path + "fileStatusMp4",{name: name}) .map(res => res.json()); } @@ -70,6 +70,32 @@ export class PostsService { return this.http.get(window.location.href + "backend/config/default.json") .map(res => res.json()); } + + deleteFile(name: string, isAudio: boolean) + { + if (isAudio) + { + return this.http.post(this.path + "deleteMp3",{name: name}) + .map(res => res.json()); + } + else + { + return this.http.post(this.path + "deleteMp4",{name: name}) + .map(res => res.json()); + } + } + + getMp3s() + { + return this.http.post(this.path + "getMp3s", {}) + .map(res => res.json()); + } + + getMp4s() + { + return this.http.post(this.path + "getMp4s", {}) + .map(res => res.json()); + } }