diff --git a/angular.json b/angular.json
index 9ab9eda..6093aa3 100644
--- a/angular.json
+++ b/angular.json
@@ -24,7 +24,8 @@
"src/backend"
],
"styles": [
- "src/styles.css"
+ "src/styles.css",
+ "../node_modules/videogular2/fonts/videogular.css"
],
"scripts": []
},
@@ -74,7 +75,8 @@
"tsConfig": "src/tsconfig.spec.json",
"scripts": [],
"styles": [
- "src/styles.css"
+ "src/styles.css",
+ "../node_modules/videogular2/fonts/videogular.css"
],
"assets": [
"src/assets",
diff --git a/package.json b/package.json
index 8aef587..7dacf09 100644
--- a/package.json
+++ b/package.json
@@ -30,6 +30,7 @@
"rxjs": "^6.5.3",
"rxjs-compat": "^6.0.0-rc.0",
"tslib": "^1.10.0",
+ "videogular2": "^7.0.1",
"zone.js": "~0.9.1"
},
"devDependencies": {
@@ -37,6 +38,7 @@
"@angular/cli": "^8.3.12",
"@angular/compiler-cli": "^8.2.11",
"@angular/language-service": "^8.2.11",
+ "@types/core-js": "^2.5.2",
"@types/file-saver": "^2.0.1",
"@types/jasmine": "2.5.45",
"@types/node": "~6.0.60",
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
new file mode 100644
index 0000000..c9d3181
--- /dev/null
+++ b/src/app/app-routing.module.ts
@@ -0,0 +1,15 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { MainComponent } from './main/main.component';
+import { PlayerComponent } from './player/player.component';
+const routes: Routes = [
+ { path: 'home', component: MainComponent },
+ { path: 'player', component: PlayerComponent},
+ { path: '', redirectTo: '/home', pathMatch: 'full' },
+];
+
+@NgModule({
+ imports: [RouterModule.forRoot(routes, { useHash: true })],
+ exports: [RouterModule]
+})
+export class AppRoutingModule { }
diff --git a/src/app/app.component.css b/src/app/app.component.css
index d0f9435..d7ef823 100644
--- a/src/app/app.component.css
+++ b/src/app/app.component.css
@@ -1,68 +1,13 @@
-.demo-card {
- margin: 16px;
-}
-
-.demo-basic {
- padding: 0;
-}
-
-.demo-basic .mat-card-content {
- padding: 16px;
-}
-
-mat-toolbar.top {
- height: 60px;
- width: 100%;
- text-align: center;
-}
-
-/*::ng-deep .mat-form-field-placeholder{
-
- transform: scale(.75) translateY(20px) !important;
- }*/
-
-.big {
- max-width: 800px;
- margin: 0 auto;
-}
-
-.centered {
- margin: 0 auto;
- top: 50%;
- left: 50%;
-}
-
-.example-full-width {
+.flex-row {
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
width: 100%;
}
-
-mat-form-field.mat-form-field {
- font-size: 24px;
- }
-
-.spinner {
- position: absolute;
- display: inline-block;
- margin-left: -28px;
- margin-top: -10px;
-}
-
-.make-room-for-spinner {
- padding-right: 40px;
-}
-
-.equal-sizes {
- padding-right: 20px;
-}
-
-.search-card-title {
- text-overflow: ellipsis;
- overflow: hidden;
- white-space: nowrap;
-}
-
-.input-clear-button {
- position: absolute;
- right: -10px;
- top: 5px;
+
+.flex-column {
+ display: flex;
+ flex-direction: column;
+ flex-basis: 100%;
+ flex: 1;
}
\ No newline at end of file
diff --git a/src/app/app.component.html b/src/app/app.component.html
index 31a405a..331f8e0 100644
--- a/src/app/app.component.html
+++ b/src/app/app.component.html
@@ -1,121 +1,15 @@
-
- |
- |
-
- {{topBarTitle}}
- |
-
+ |
-
+
+
-
-
-
-
-
- Youtube Downloader
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Audio
-
-
- Your audio files are here
-
-
- 0;else nomp3s">
-
-
-
-
-
-
-
-
-
-
-
- Video
-
-
- Your video files are here
-
-
- 0;else nomp4s">
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/src/app/app.component.ts b/src/app/app.component.ts
index 5b1a843..b256e73 100644
--- a/src/app/app.component.ts
+++ b/src/app/app.component.ts
@@ -15,6 +15,7 @@ import 'rxjs/add/operator/debounceTime'
import 'rxjs/add/operator/do'
import 'rxjs/add/operator/switch'
import { YoutubeSearchService, Result } from './youtube-search.service';
+import { Router } from '@angular/router';
@Component({
selector: 'app-root',
@@ -54,282 +55,26 @@ export class AppComponent implements OnInit {
@ViewChild('urlinput', { read: ElementRef, static: false }) urlInput: ElementRef;
- constructor(private postsService: PostsService, private youtubeSearch: YoutubeSearchService, public snackBar: MatSnackBar) {
+ constructor(private postsService: PostsService, private youtubeSearch: YoutubeSearchService, public snackBar: MatSnackBar,
+ public router: Router) {
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.baseStreamPath = result['YoutubeDLMaterial']['Downloader']['path-base'];
- this.audioFolderPath = result['YoutubeDLMaterial']['Downloader']['path-audio'];
- this.videoFolderPath = result['YoutubeDLMaterial']['Downloader']['path-video'];
- this.youtubeSearchEnabled = result['YoutubeDLMaterial']['API'] && result['YoutubeDLMaterial']['API']['use_youtube_API'];
- this.youtubeAPIKey = this.youtubeSearchEnabled ? result['YoutubeDLMaterial']['API']['youtube_API_key'] : null;
-
- this.postsService.path = backendUrl;
- this.postsService.startPath = backendUrl;
- this.postsService.startPathSSL = backendUrl;
-
- if (this.fileManagerEnabled) {
- this.getMp3s();
- this.getMp4s();
- }
-
- if (this.youtubeSearchEnabled && this.youtubeAPIKey) {
- this.youtubeSearch.initializeAPI(this.youtubeAPIKey);
- this.attachToInput();
- }
}, error => {
console.log(error);
});
}
- // file manager stuff
-
- getMp3s() {
- this.postsService.getMp3s().subscribe(result => {
- const mp3s = result['mp3s'];
- this.mp3s = mp3s;
- }, error => {
- console.log(error);
- });
- }
-
- getMp4s() {
- this.postsService.getMp4s().subscribe(result => {
- const mp4s = result['mp4s'];
- this.mp4s = mp4s;
- },
- error => {
- console.log(error);
- });
- }
-
- public goToFile(name, isAudio) {
- if (isAudio) {
- this.downloadHelperMp3(name, false, true);
- } else {
- this.downloadHelperMp4(name, false, true);
- }
- }
-
- public removeFromMp3(name: string) {
- for (let i = 0; i < this.mp3s.length; i++) {
- if (this.mp3s[i].id === name) {
- this.mp3s.splice(i, 1);
- }
- }
- }
-
- public removeFromMp4(name: string) {
- // console.log(name);
- // console.log(this.mp4s);
- for (let i = 0; i < this.mp4s.length; i++) {
- if (this.mp4s[i].id === name) {
- this.mp4s.splice(i, 1);
- }
- }
- }
-
- // app initialization.
ngOnInit() {
- this.iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window['MSStream'];
+
}
- // download helpers
-
- downloadHelperMp3(name, is_playlist = false, forceView = false) {
- this.downloadingfile = false;
-
- // if download only mode, just download the file. no redirect
- if (forceView === false && this.downloadOnlyMode && !this.iOS) {
- if (is_playlist) {
- for (let i = 0; i < name.length; i++) {
- this.downloadAudioFile(name[i]);
- }
- } else {
- this.downloadAudioFile(name);
- }
- } else {
- if (is_playlist) {
- window.location.href = this.baseStreamPath + this.audioFolderPath + name[0] + '.mp3';
- } else {
- window.location.href = this.baseStreamPath + this.audioFolderPath + name + '.mp3';
- }
- }
-
- // reloads mp3s
- if (this.fileManagerEnabled) {
- this.getMp3s();
- }
- }
-
- downloadHelperMp4(name, is_playlist = false, forceView = false) {
- this.downloadingfile = false;
-
- // if download only mode, just download the file. no redirect
- if (forceView === false && this.downloadOnlyMode) {
- if (is_playlist) {
- for (let i = 0; i < name.length; i++) {
- this.downloadVideoFile(name[i]);
- }
- } else {
- this.downloadVideoFile(name);
- }
- } else {
- if (is_playlist) {
- window.location.href = this.baseStreamPath + this.videoFolderPath + name[0] + '.mp4';
- } else {
- window.location.href = this.baseStreamPath + this.videoFolderPath + name + '.mp4';
- }
- }
-
- // reloads mp4s
- if (this.fileManagerEnabled) {
- this.getMp4s();
- }
- }
-
- // download click handler
- downloadClicked() {
- if (this.ValidURL(this.url)) {
- this.urlError = false;
- this.path = '';
-
- if (this.audioOnly) {
- this.downloadingfile = true;
- this.postsService.makeMP3(this.url).subscribe(posts => {
- const is_playlist = !!(posts['file_names']);
- this.path = is_playlist ? posts['file_names'] : posts['audiopathEncoded'];
- if (this.path !== '-1') {
- this.downloadHelperMp3(this.path, is_playlist);
- }
- }, error => { // can't access server
- this.downloadingfile = false;
- this.openSnackBar('Download failed!', 'OK.');
- });
- } else {
- this.downloadingfile = true;
- this.postsService.makeMP4(this.url).subscribe(posts => {
- const is_playlist = !!(posts['file_names']);
- this.path = is_playlist ? posts['file_names'] : posts['videopathEncoded'];
- if (this.path !== '-1') {
- this.downloadHelperMp4(this.path, is_playlist);
- }
- }, error => { // can't access server
- this.downloadingfile = false;
- this.openSnackBar('Download failed!', 'OK.');
- });
- }
- } else {
- this.urlError = true;
- }
- }
-
- downloadAudioFile(name) {
- this.postsService.downloadFileFromServer(name, 'audio').subscribe(res => {
- const blob: Blob = res;
- saveAs(blob, name + '.mp3');
-
- // tell server to delete the file once downloaded
- this.postsService.deleteFile(name, true).subscribe(delRes => {
-
- });
- });
- }
-
- downloadVideoFile(name) {
- this.postsService.downloadFileFromServer(name, 'video').subscribe(res => {
- const blob: Blob = res;
- saveAs(blob, name + '.mp4');
-
- // tell server to delete the file once downloaded
- this.postsService.deleteFile(name, false).subscribe(delRes => {
-
- });
- });
- }
-
- clearInput() {
- this.url = '';
- this.results_showing = false;
- }
-
- onInputBlur() {
- this.results_showing = false;
- }
-
- visitURL(url) {
- window.open(url);
- }
-
- useURL(url) {
- this.results_showing = false;
- this.url = url;
- }
-
- inputChanged(new_val) {
- if (new_val === '') {
- this.results_showing = false;
- } else {
- if (this.ValidURL(new_val)) {
- this.results_showing = false;
- }
- }
- }
-
- // 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]*))?)/;
- const re = new RegExp(strRegex);
- return re.test(str);
- }
-
- // snackbar helper
- public openSnackBar(message: string, action: string) {
- this.snackBar.open(message, action, {
- duration: 2000,
- });
- }
-
- attachToInput() {
- Observable.fromEvent(this.urlInput.nativeElement, 'keyup')
- .map((e: any) => e.target.value) // extract the value of input
- .filter((text: string) => text.length > 1) // filter out if empty
- .debounceTime(250) // only once every 250ms
- .do(() => this.results_loading = true) // enable loading
- .map((query: string) => this.youtubeSearch.search(query))
- .switch() // act on the return of the search
- .subscribe(
- (results: Result[]) => {
- // console.log(results);
- this.results_loading = false;
- if (results && results.length > 0) {
- this.results = results;
- this.results_showing = true;
- } else {
- this.results_showing = false;
- }
- },
- (err: any) => {
- console.log(err)
- this.results_loading = false;
- this.results_showing = false;
- },
- () => { // on completion
- this.results_loading = false;
- }
- );
- }
-
- onResize(event) {
- this.files_cols = (event.target.innerWidth <= 450) ? 2 : 4;
+ goBack() {
+ this.router.navigate(['/home']);
}
}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index fb6fd9e..d5aff3a 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -4,7 +4,8 @@ import {MatNativeDateModule, MatRadioModule, MatInputModule, MatButtonModule, Ma
MatSnackBarModule, MatCardModule, MatSelectModule, MatToolbarModule, MatCheckboxModule, MatGridListModule,
MatProgressBarModule, MatExpansionModule,
MatGridList,
- MatProgressSpinnerModule} from '@angular/material';
+ MatProgressSpinnerModule,
+ MatButtonToggleModule} from '@angular/material';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import { AppComponent } from './app.component';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
@@ -14,11 +15,20 @@ import { PostsService } from 'app/posts.services';
import {APP_BASE_HREF} from '@angular/common';
import { FileCardComponent } from './file-card/file-card.component';
import {RouterModule} from '@angular/router';
+import { AppRoutingModule } from './app-routing.module';
+import { MainComponent } from './main/main.component';
+import { PlayerComponent } from './player/player.component';
+import {VgCoreModule} from 'videogular2/compiled/core';
+import {VgControlsModule} from 'videogular2/compiled/controls';
+import {VgOverlayPlayModule} from 'videogular2/compiled/overlay-play';
+import {VgBufferingModule} from 'videogular2/compiled/buffering';
@NgModule({
declarations: [
AppComponent,
- FileCardComponent
+ FileCardComponent,
+ MainComponent,
+ PlayerComponent
],
imports: [
BrowserModule,
@@ -43,7 +53,13 @@ import {RouterModule} from '@angular/router';
MatExpansionModule,
MatProgressBarModule,
MatProgressSpinnerModule,
- RouterModule
+ MatButtonToggleModule,
+ VgCoreModule,
+ VgControlsModule,
+ VgOverlayPlayModule,
+ VgBufferingModule,
+ RouterModule,
+ AppRoutingModule
],
providers: [PostsService],
bootstrap: [AppComponent]
diff --git a/src/app/file-card/file-card.component.html b/src/app/file-card/file-card.component.html
index 8513572..ddb48e0 100644
--- a/src/app/file-card/file-card.component.html
+++ b/src/app/file-card/file-card.component.html
@@ -1,7 +1,7 @@
diff --git a/src/app/file-card/file-card.component.ts b/src/app/file-card/file-card.component.ts
index ce18ca6..f35ea4e 100644
--- a/src/app/file-card/file-card.component.ts
+++ b/src/app/file-card/file-card.component.ts
@@ -1,8 +1,8 @@
import { Component, OnInit, Input, Output } from '@angular/core';
import {PostsService} from '../posts.services';
import {MatSnackBar} from '@angular/material';
-import {AppComponent} from '../app.component';
import {EventEmitter} from '@angular/core';
+import { MainComponent } from 'app/main/main.component';
@Component({
selector: 'app-file-card',
@@ -18,7 +18,7 @@ export class FileCardComponent implements OnInit {
@Input() isAudio = true;
@Output() removeFile: EventEmitter = new EventEmitter();
- constructor(private postsService: PostsService, public snackBar: MatSnackBar, public appComponent: AppComponent) { }
+ constructor(private postsService: PostsService, public snackBar: MatSnackBar, public mainComponent: MainComponent) { }
ngOnInit() {
}
diff --git a/src/app/main/main.component.css b/src/app/main/main.component.css
new file mode 100644
index 0000000..d0f9435
--- /dev/null
+++ b/src/app/main/main.component.css
@@ -0,0 +1,68 @@
+.demo-card {
+ margin: 16px;
+}
+
+.demo-basic {
+ padding: 0;
+}
+
+.demo-basic .mat-card-content {
+ padding: 16px;
+}
+
+mat-toolbar.top {
+ height: 60px;
+ width: 100%;
+ text-align: center;
+}
+
+/*::ng-deep .mat-form-field-placeholder{
+
+ transform: scale(.75) translateY(20px) !important;
+ }*/
+
+.big {
+ max-width: 800px;
+ margin: 0 auto;
+}
+
+.centered {
+ margin: 0 auto;
+ top: 50%;
+ left: 50%;
+}
+
+.example-full-width {
+ width: 100%;
+}
+
+mat-form-field.mat-form-field {
+ font-size: 24px;
+ }
+
+.spinner {
+ position: absolute;
+ display: inline-block;
+ margin-left: -28px;
+ margin-top: -10px;
+}
+
+.make-room-for-spinner {
+ padding-right: 40px;
+}
+
+.equal-sizes {
+ padding-right: 20px;
+}
+
+.search-card-title {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+}
+
+.input-clear-button {
+ position: absolute;
+ right: -10px;
+ top: 5px;
+}
\ No newline at end of file
diff --git a/src/app/main/main.component.html b/src/app/main/main.component.html
new file mode 100644
index 0000000..f173278
--- /dev/null
+++ b/src/app/main/main.component.html
@@ -0,0 +1,107 @@
+
+
+
+
+ Youtube Downloader
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Audio
+
+
+ Your audio files are here
+
+
+ 0;else nomp3s">
+
+
+
+
+
+
+
+
+
+
+
+ Video
+
+
+ Your video files are here
+
+
+ 0;else nomp4s">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/app/main/main.component.spec.ts b/src/app/main/main.component.spec.ts
new file mode 100644
index 0000000..0878044
--- /dev/null
+++ b/src/app/main/main.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { MainComponent } from './main.component';
+
+describe('MainComponent', () => {
+ let component: MainComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ MainComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(MainComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/main/main.component.ts b/src/app/main/main.component.ts
new file mode 100644
index 0000000..b9a5b7c
--- /dev/null
+++ b/src/app/main/main.component.ts
@@ -0,0 +1,338 @@
+import { Component, OnInit, ElementRef, ViewChild } 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';
+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';
+import 'rxjs/add/observable/fromEvent'
+import 'rxjs/add/operator/filter'
+import 'rxjs/add/operator/debounceTime'
+import 'rxjs/add/operator/do'
+import 'rxjs/add/operator/switch'
+import { YoutubeSearchService, Result } from '../youtube-search.service';
+import { Router } from '@angular/router';
+
+@Component({
+ selector: 'app-root',
+ templateUrl: './main.component.html',
+ styleUrls: ['./main.component.css']
+})
+export class MainComponent implements OnInit {
+ iOS = false;
+
+ determinateProgress = false;
+ downloadingfile = false;
+ audioOnly: boolean;
+ urlError = false;
+ path = '';
+ url = '';
+ exists = '';
+ percentDownloaded: number;
+ fileManagerEnabled = false;
+ downloadOnlyMode = false;
+ baseStreamPath;
+ audioFolderPath;
+ videoFolderPath;
+
+ // youtube api
+ youtubeSearchEnabled = false;
+ youtubeAPIKey = null;
+ results_loading = false;
+ results_showing = true;
+ results = [];
+
+ mp3s: any[] = [];
+ mp4s: any[] = [];
+ files_cols = (window.innerWidth <= 450) ? 2 : 4;
+
+ urlForm = new FormControl('', [Validators.required]);
+
+ @ViewChild('urlinput', { read: ElementRef, static: false }) urlInput: ElementRef;
+
+ constructor(private postsService: PostsService, private youtubeSearch: YoutubeSearchService, public snackBar: MatSnackBar,
+ private router: Router) {
+ this.audioOnly = false;
+
+
+ // loading config
+ this.postsService.loadNavItems().subscribe(result => { // loads settings
+ const backendUrl = result['YoutubeDLMaterial']['Host']['backendurl'];
+ this.fileManagerEnabled = result['YoutubeDLMaterial']['Extra']['file_manager_enabled'];
+ this.downloadOnlyMode = result['YoutubeDLMaterial']['Extra']['download_only_mode'];
+ this.baseStreamPath = result['YoutubeDLMaterial']['Downloader']['path-base'];
+ this.audioFolderPath = result['YoutubeDLMaterial']['Downloader']['path-audio'];
+ this.videoFolderPath = result['YoutubeDLMaterial']['Downloader']['path-video'];
+ this.youtubeSearchEnabled = result['YoutubeDLMaterial']['API'] && result['YoutubeDLMaterial']['API']['use_youtube_API'];
+ this.youtubeAPIKey = this.youtubeSearchEnabled ? result['YoutubeDLMaterial']['API']['youtube_API_key'] : null;
+
+ this.postsService.path = backendUrl;
+ this.postsService.startPath = backendUrl;
+ this.postsService.startPathSSL = backendUrl;
+
+ if (this.fileManagerEnabled) {
+ this.getMp3s();
+ this.getMp4s();
+ }
+
+ if (this.youtubeSearchEnabled && this.youtubeAPIKey) {
+ this.youtubeSearch.initializeAPI(this.youtubeAPIKey);
+ this.attachToInput();
+ }
+ }, error => {
+ console.log(error);
+ });
+
+ }
+
+ // file manager stuff
+
+ getMp3s() {
+ this.postsService.getMp3s().subscribe(result => {
+ const mp3s = result['mp3s'];
+ this.mp3s = mp3s;
+ }, error => {
+ console.log(error);
+ });
+ }
+
+ getMp4s() {
+ this.postsService.getMp4s().subscribe(result => {
+ const mp4s = result['mp4s'];
+ this.mp4s = mp4s;
+ },
+ error => {
+ console.log(error);
+ });
+ }
+
+ public goToFile(name, isAudio) {
+ if (isAudio) {
+ this.downloadHelperMp3(name, false, true);
+ } else {
+ this.downloadHelperMp4(name, false, true);
+ }
+ }
+
+ public removeFromMp3(name: string) {
+ for (let i = 0; i < this.mp3s.length; i++) {
+ if (this.mp3s[i].id === name) {
+ this.mp3s.splice(i, 1);
+ }
+ }
+ }
+
+ public removeFromMp4(name: string) {
+ // console.log(name);
+ // console.log(this.mp4s);
+ for (let i = 0; i < this.mp4s.length; i++) {
+ if (this.mp4s[i].id === name) {
+ this.mp4s.splice(i, 1);
+ }
+ }
+ }
+
+ // app initialization.
+ ngOnInit() {
+ this.iOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window['MSStream'];
+ }
+
+ // download helpers
+
+ downloadHelperMp3(name, is_playlist = false, forceView = false) {
+ this.downloadingfile = false;
+
+ // if download only mode, just download the file. no redirect
+ if (forceView === false && this.downloadOnlyMode && !this.iOS) {
+ if (is_playlist) {
+ for (let i = 0; i < name.length; i++) {
+ this.downloadAudioFile(name[i]);
+ }
+ } else {
+ this.downloadAudioFile(name);
+ }
+ } else {
+ if (is_playlist) {
+ this.router.navigate(['/player', {fileNames: name.join('|nvr|'), type: 'audio'}]);
+ // window.location.href = this.baseStreamPath + this.audioFolderPath + name[0] + '.mp3';
+ } else {
+ window.location.href = this.baseStreamPath + this.audioFolderPath + name + '.mp3';
+ }
+ }
+
+ // reloads mp3s
+ if (this.fileManagerEnabled) {
+ this.getMp3s();
+ }
+ }
+
+ downloadHelperMp4(name, is_playlist = false, forceView = false) {
+ this.downloadingfile = false;
+
+ // if download only mode, just download the file. no redirect
+ if (forceView === false && this.downloadOnlyMode) {
+ if (is_playlist) {
+ for (let i = 0; i < name.length; i++) {
+ this.downloadVideoFile(name[i]);
+ }
+ } else {
+ this.downloadVideoFile(name);
+ }
+ } else {
+ if (is_playlist) {
+ this.router.navigate(['/player', {fileNames: name.join('|nvr|'), type: 'video'}]);
+ // window.location.href = this.baseStreamPath + this.videoFolderPath + name[0] + '.mp4';
+ } else {
+ this.router.navigate(['/player', {fileNames: name, type: 'video'}]);
+ // window.location.href = this.baseStreamPath + this.videoFolderPath + name + '.mp4';
+ }
+ }
+
+ // reloads mp4s
+ if (this.fileManagerEnabled) {
+ this.getMp4s();
+ }
+ }
+
+ // download click handler
+ downloadClicked() {
+ if (this.ValidURL(this.url)) {
+ this.urlError = false;
+ this.path = '';
+
+ if (this.audioOnly) {
+ this.downloadingfile = true;
+ this.postsService.makeMP3(this.url).subscribe(posts => {
+ const is_playlist = !!(posts['file_names']);
+ this.path = is_playlist ? posts['file_names'] : posts['audiopathEncoded'];
+ if (this.path !== '-1') {
+ this.downloadHelperMp3(this.path, is_playlist);
+ }
+ }, error => { // can't access server
+ this.downloadingfile = false;
+ this.openSnackBar('Download failed!', 'OK.');
+ });
+ } else {
+ this.downloadingfile = true;
+ this.postsService.makeMP4(this.url).subscribe(posts => {
+ const is_playlist = !!(posts['file_names']);
+ this.path = is_playlist ? posts['file_names'] : posts['videopathEncoded'];
+ if (this.path !== '-1') {
+ this.downloadHelperMp4(this.path, is_playlist);
+ }
+ }, error => { // can't access server
+ this.downloadingfile = false;
+ this.openSnackBar('Download failed!', 'OK.');
+ });
+ }
+ } else {
+ this.urlError = true;
+ }
+ }
+
+ downloadAudioFile(name) {
+ this.postsService.downloadFileFromServer(name, 'audio').subscribe(res => {
+ const blob: Blob = res;
+ saveAs(blob, name + '.mp3');
+
+ // tell server to delete the file once downloaded
+ this.postsService.deleteFile(name, true).subscribe(delRes => {
+
+ });
+ });
+ }
+
+ downloadVideoFile(name) {
+ this.postsService.downloadFileFromServer(name, 'video').subscribe(res => {
+ const blob: Blob = res;
+ saveAs(blob, name + '.mp4');
+
+ // tell server to delete the file once downloaded
+ this.postsService.deleteFile(name, false).subscribe(delRes => {
+
+ });
+ });
+ }
+
+ clearInput() {
+ this.url = '';
+ this.results_showing = false;
+ }
+
+ onInputBlur() {
+ this.results_showing = false;
+ }
+
+ visitURL(url) {
+ window.open(url);
+ }
+
+ useURL(url) {
+ this.results_showing = false;
+ this.url = url;
+ }
+
+ inputChanged(new_val) {
+ if (new_val === '') {
+ this.results_showing = false;
+ } else {
+ if (this.ValidURL(new_val)) {
+ this.results_showing = false;
+ }
+ }
+ }
+
+ // 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]*))?)/;
+ const re = new RegExp(strRegex);
+ return re.test(str);
+ }
+
+ // snackbar helper
+ public openSnackBar(message: string, action: string) {
+ this.snackBar.open(message, action, {
+ duration: 2000,
+ });
+ }
+
+ attachToInput() {
+ Observable.fromEvent(this.urlInput.nativeElement, 'keyup')
+ .map((e: any) => e.target.value) // extract the value of input
+ .filter((text: string) => text.length > 1) // filter out if empty
+ .debounceTime(250) // only once every 250ms
+ .do(() => this.results_loading = true) // enable loading
+ .map((query: string) => this.youtubeSearch.search(query))
+ .switch() // act on the return of the search
+ .subscribe(
+ (results: Result[]) => {
+ // console.log(results);
+ this.results_loading = false;
+ if (results && results.length > 0) {
+ this.results = results;
+ this.results_showing = true;
+ } else {
+ this.results_showing = false;
+ }
+ },
+ (err: any) => {
+ console.log(err)
+ this.results_loading = false;
+ this.results_showing = false;
+ },
+ () => { // on completion
+ this.results_loading = false;
+ }
+ );
+ }
+
+ onResize(event) {
+ this.files_cols = (event.target.innerWidth <= 450) ? 2 : 4;
+ }
+}
+
diff --git a/src/app/player/player.component.css b/src/app/player/player.component.css
new file mode 100644
index 0000000..266c172
--- /dev/null
+++ b/src/app/player/player.component.css
@@ -0,0 +1,19 @@
+.video-player {
+ width: 100%;
+ margin: 0 auto;
+}
+
+.audio-styles {
+ height: 50px;
+ background-color: transparent;
+}
+
+.video-styles {
+
+}
+
+::ng-deep .mat-button-toggle-label-content {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+}
\ No newline at end of file
diff --git a/src/app/player/player.component.html b/src/app/player/player.component.html
new file mode 100644
index 0000000..2526df5
--- /dev/null
+++ b/src/app/player/player.component.html
@@ -0,0 +1,14 @@
+ 0; else loading">
+
+
+
+
+
+
+
+
+ {{name}}
+
+
+
\ No newline at end of file
diff --git a/src/app/player/player.component.spec.ts b/src/app/player/player.component.spec.ts
new file mode 100644
index 0000000..d08e5be
--- /dev/null
+++ b/src/app/player/player.component.spec.ts
@@ -0,0 +1,25 @@
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { PlayerComponent } from './player.component';
+
+describe('PlayerComponent', () => {
+ let component: PlayerComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ PlayerComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PlayerComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/player/player.component.ts b/src/app/player/player.component.ts
new file mode 100644
index 0000000..93fd1d1
--- /dev/null
+++ b/src/app/player/player.component.ts
@@ -0,0 +1,108 @@
+import { Component, OnInit } from '@angular/core';
+import { VgAPI } from 'videogular2/compiled/core';
+import { PostsService } from 'app/posts.services';
+import { ActivatedRoute } from '@angular/router';
+
+export interface IMedia {
+ title: string;
+ src: string;
+ type: string;
+}
+
+@Component({
+ selector: 'app-player',
+ templateUrl: './player.component.html',
+ styleUrls: ['./player.component.css']
+})
+export class PlayerComponent implements OnInit {
+
+ playlist: Array = [];
+
+ currentIndex = 0;
+ currentItem: IMedia = null;
+ api: VgAPI;
+
+ // params
+ fileNames: string[];
+ type: string;
+
+ baseStreamPath = null;
+ audioFolderPath = null;
+ videoFolderPath = null;
+
+ ngOnInit(): void {
+ this.fileNames = this.route.snapshot.paramMap.get('fileNames').split('|nvr|');
+ this.type = this.route.snapshot.paramMap.get('type');
+
+ // loading config
+ this.postsService.loadNavItems().subscribe(result => { // loads settings
+ this.baseStreamPath = result['YoutubeDLMaterial']['Downloader']['path-base'];
+ this.audioFolderPath = result['YoutubeDLMaterial']['Downloader']['path-audio'];
+ this.videoFolderPath = result['YoutubeDLMaterial']['Downloader']['path-video'];
+ let fileType = null;
+ if (this.type === 'audio') {
+ fileType = 'audio/mp3';
+ } else if (this.type === 'video') {
+ fileType = 'video/mp4';
+ } else {
+ // error
+ console.error('Must have valid file type! Use \'audio\' or \video\'');
+ }
+
+ for (let i = 0; i < this.fileNames.length; i++) {
+ const fileName = this.fileNames[i];
+ const baseLocation = (this.type === 'audio') ? this.audioFolderPath : this.videoFolderPath;
+ const fullLocation = this.baseStreamPath + baseLocation + fileName + (this.type === 'audio' ? '.mp3' : '.mp4');
+ const mediaObject: IMedia = {
+ title: fileName,
+ src: fullLocation,
+ type: fileType
+ }
+ console.log(mediaObject);
+ this.playlist.push(mediaObject);
+ }
+ this.currentItem = this.playlist[this.currentIndex];
+ });
+
+ this.getFileInfos();
+
+ }
+
+ constructor(private postsService: PostsService, private route: ActivatedRoute) {
+ }
+
+ onPlayerReady(api: VgAPI) {
+ this.api = api;
+
+ this.api.getDefaultMedia().subscriptions.loadedMetadata.subscribe(this.playVideo.bind(this));
+ this.api.getDefaultMedia().subscriptions.ended.subscribe(this.nextVideo.bind(this));
+ }
+
+ nextVideo() {
+ if (this.currentIndex === this.playlist.length - 1) {
+ // dont continue playing
+ // this.currentIndex = 0;
+ return;
+ }
+
+ this.currentIndex++;
+ this.currentItem = this.playlist[ this.currentIndex ];
+ }
+
+ playVideo() {
+ this.api.play();
+ }
+
+ onClickPlaylistItem(item: IMedia, index: number) {
+ console.log('new current item is ' + item.title + ' at index ' + index);
+ this.currentIndex = index;
+ this.currentItem = item;
+ }
+
+ getFileInfos() {
+ this.postsService.getFileInfo(this.fileNames, this.type).subscribe(res => {
+
+ });
+ }
+
+}
diff --git a/src/app/posts.services.ts b/src/app/posts.services.ts
index 32943cc..05e79a3 100644
--- a/src/app/posts.services.ts
+++ b/src/app/posts.services.ts
@@ -79,6 +79,10 @@ export class PostsService {
downloadFileFromServer(fileName, type) {
return this.http.post(this.path + 'downloadFile', {fileName: fileName, type: type}, {responseType: 'blob'});
}
+
+ getFileInfo(fileNames, type) {
+ return this.http.post(this.path + 'getVideoInfos', {fileNames: fileNames, type: type});
+ }
}