diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index aeb280f..fee461e 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -4,6 +4,8 @@ import { HomeComponent } from './components/home/home.component'; import { ArtistsComponent } from './components/artists/artists.component'; import { AlbumsComponent } from './components/albums/albums.component'; import { SearchComponent } from './components/search/search.component'; +import { AddsongComponent } from './components/addsong/addsong.component'; +import { DownloadsComponent } from './components/downloads/downloads.component'; const routes: Routes = [ { path: '', component: HomeComponent }, @@ -11,7 +13,9 @@ const routes: Routes = [ { path: 'artists', component: ArtistsComponent }, { path: 'albums/:id', component: AlbumsComponent }, { path: 'albums', component: AlbumsComponent }, - { path: 'search/:query', component: SearchComponent } + { path: 'search/:query', component: SearchComponent }, + { path: 'add/song', component: AddsongComponent }, + { path: 'downloads', component: DownloadsComponent } ]; @NgModule({ diff --git a/src/app/app.component.html b/src/app/app.component.html index 3765b52..161961f 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -7,8 +7,8 @@ Explore - - Songs + + Add Song @@ -42,5 +42,22 @@ + + + + + + {{ item.user }} + 11 mins ago + + × + + + + {{ item.message }} + + + + diff --git a/src/app/app.component.scss b/src/app/app.component.scss index 38a1a62..3bb4f41 100644 --- a/src/app/app.component.scss +++ b/src/app/app.component.scss @@ -1,4 +1,4 @@ -// Main +// Main body { overflow-x: hidden; } @@ -15,14 +15,14 @@ app-controls { // Sidebar #sidebar-wrapper { - min-height: inherit; + min-height: inherit; margin-left: -15rem; -webkit-transition: margin .25s ease-out; -moz-transition: margin .25s ease-out; -o-transition: margin .25s ease-out; transition: margin .25s ease-out; } - + #sidebar-wrapper .sidebar-heading { padding: 0.875rem 1.25rem; font-size: 1.2rem; @@ -31,7 +31,7 @@ app-controls { .sidebar-heading > img { width: 64px; } - + #sidebar-wrapper .list-group { width: 15rem; } @@ -48,7 +48,7 @@ app-controls { #navbar-search { display: none; } - + @media (min-width: 768px) { #sidebar-wrapper { margin-left: 0; @@ -62,8 +62,12 @@ app-controls { min-width: 0; width: 100%; } - + #wrapper.toggled #sidebar-wrapper { margin-left: -15rem; } -} \ No newline at end of file +} + +.toast { + opacity: 1; +} diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 28c3aa5..75b75e5 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -2,6 +2,7 @@ import { Component, OnInit } from '@angular/core'; import { faGlobeEurope, faMusic, faCompactDisc, faUsers, faCog, faChevronLeft, faChevronRight } from '@fortawesome/free-solid-svg-icons'; import { Router } from '@angular/router'; import { FormBuilder } from '@angular/forms'; +import { NotificationService } from './services/notification.service'; @Component({ selector: 'app-root', @@ -26,7 +27,8 @@ export class AppComponent implements OnInit { constructor( public router: Router, - private fb: FormBuilder + private fb: FormBuilder, + public notification: NotificationService ) { } ngOnInit() { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index ecd9ad9..9ff61d6 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -13,6 +13,8 @@ import { AlbumsComponent } from './components/albums/albums.component'; import { ControlsComponent } from './components/controls/controls.component'; import { SonglistComponent } from './components/songlist/songlist.component'; import { SearchComponent } from './components/search/search.component'; +import { AddsongComponent } from './components/addsong/addsong.component'; +import { DownloadsComponent } from './components/downloads/downloads.component'; @NgModule({ declarations: [ @@ -22,7 +24,9 @@ import { SearchComponent } from './components/search/search.component'; AlbumsComponent, ControlsComponent, SonglistComponent, - SearchComponent + SearchComponent, + AddsongComponent, + DownloadsComponent ], imports: [ BrowserModule, diff --git a/src/app/components/addsong/addsong.component.html b/src/app/components/addsong/addsong.component.html new file mode 100644 index 0000000..273f5f7 --- /dev/null +++ b/src/app/components/addsong/addsong.component.html @@ -0,0 +1,4 @@ + + + + diff --git a/src/app/components/addsong/addsong.component.scss b/src/app/components/addsong/addsong.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/components/addsong/addsong.component.spec.ts b/src/app/components/addsong/addsong.component.spec.ts new file mode 100644 index 0000000..bebd023 --- /dev/null +++ b/src/app/components/addsong/addsong.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AddsongComponent } from './addsong.component'; + +describe('AddsongComponent', () => { + let component: AddsongComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ AddsongComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AddsongComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/addsong/addsong.component.ts b/src/app/components/addsong/addsong.component.ts new file mode 100644 index 0000000..91f16dc --- /dev/null +++ b/src/app/components/addsong/addsong.component.ts @@ -0,0 +1,27 @@ +import { Component, OnInit } from '@angular/core'; +import { DownloadService } from 'src/app/services/download.service'; +import { FormBuilder } from '@angular/forms'; + +@Component({ + selector: 'app-addsong', + templateUrl: './addsong.component.html', + styleUrls: ['./addsong.component.scss'] +}) +export class AddsongComponent implements OnInit { + + form = this.fb.group({ + id: '' + }); + + constructor( + private download: DownloadService, + private fb: FormBuilder + ) { } + + ngOnInit() { } + + onSubmit() { + this.download.addQueueItem(this.form.value.id); + } + +} diff --git a/src/app/components/downloads/downloads.component.html b/src/app/components/downloads/downloads.component.html new file mode 100644 index 0000000..684bb9f --- /dev/null +++ b/src/app/components/downloads/downloads.component.html @@ -0,0 +1,9 @@ + + + {{ item.info.title }} + + + + + diff --git a/src/app/components/downloads/downloads.component.scss b/src/app/components/downloads/downloads.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/app/components/downloads/downloads.component.spec.ts b/src/app/components/downloads/downloads.component.spec.ts new file mode 100644 index 0000000..e7a1fa6 --- /dev/null +++ b/src/app/components/downloads/downloads.component.spec.ts @@ -0,0 +1,25 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DownloadsComponent } from './downloads.component'; + +describe('DownloadsComponent', () => { + let component: DownloadsComponent; + let fixture: ComponentFixture; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ DownloadsComponent ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(DownloadsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/components/downloads/downloads.component.ts b/src/app/components/downloads/downloads.component.ts new file mode 100644 index 0000000..e792019 --- /dev/null +++ b/src/app/components/downloads/downloads.component.ts @@ -0,0 +1,38 @@ +import { Component, OnInit } from '@angular/core'; +import { DownloadService } from 'src/app/services/download.service'; +import { AxiosResponse } from 'axios'; +import { ApiData } from 'src/app/services/data.service'; +import { DomSanitizer } from '@angular/platform-browser'; + +@Component({ + selector: 'app-downloads', + templateUrl: './downloads.component.html', + styleUrls: ['./downloads.component.scss'] +}) +export class DownloadsComponent implements OnInit { + + downloadQueue: AxiosResponse; + + constructor( + public download: DownloadService, + private sanitizer: DomSanitizer + ) { } + + ngOnInit() { + this.getDownloadQueue(); + setInterval(() => { + this.getDownloadQueue(); + }, 1000); + } + + getDownloadQueue() { + this.download.getDownloadQueue().then(res => { + this.downloadQueue = res; + }); + } + + getProgress(val) { + return this.sanitizer.bypassSecurityTrustStyle(`${val}%`); + } + +} diff --git a/src/app/components/home/home.component.ts b/src/app/components/home/home.component.ts index 7d436ae..f78812b 100644 --- a/src/app/components/home/home.component.ts +++ b/src/app/components/home/home.component.ts @@ -11,7 +11,6 @@ import { ApiService } from 'src/app/services/api.service'; export class HomeComponent implements OnInit { songs: Promise; - artists: Artist[] = []; constructor( private api: ApiService, diff --git a/src/app/services/data.service.ts b/src/app/services/data.service.ts index 91c9b07..2b84200 100644 --- a/src/app/services/data.service.ts +++ b/src/app/services/data.service.ts @@ -24,9 +24,7 @@ export class DataService { private axiosInstance: AxiosInstance; constructor() { - this.axiosInstance = axios.create({ - timeout: 3000 - }); + this.axiosInstance = axios.create(); } /** * @param type The type of the thing you want to get. The type is a string containing either 'artist', 'album' or 'song'. @@ -85,4 +83,12 @@ export class DataService { search(query: string): Promise> { return this.axiosInstance.get(`${this.apiUrl}/search/${query}`); } + + download(id: string): Promise> { + return this.axiosInstance.get(`${this.apiUrl}/download/${id}`); + } + + downloadqueue(): Promise> { + return this.axiosInstance.get(`${this.apiUrl}/downloadqueue`); + } } diff --git a/src/app/services/download.service.spec.ts b/src/app/services/download.service.spec.ts new file mode 100644 index 0000000..8a29a96 --- /dev/null +++ b/src/app/services/download.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { DownloadService } from './download.service'; + +describe('DownloadService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: DownloadService = TestBed.get(DownloadService); + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/services/download.service.ts b/src/app/services/download.service.ts new file mode 100644 index 0000000..9cf00da --- /dev/null +++ b/src/app/services/download.service.ts @@ -0,0 +1,47 @@ +import { Injectable } from '@angular/core'; +import { DataService } from './data.service'; +import { AxiosResponse } from 'axios'; +import { NotificationService } from './notification.service'; + +@Injectable({ + providedIn: 'root' +}) +export class DownloadService { + + constructor( + private data: DataService, + private notification: NotificationService + ) { } + + downloadQueue: Promise[] = []; + + addQueueItem(id: string) { + const index = this.downloadQueue.push(this.data.download(id)) - 1; + this.downloadQueue[index].then(res => { + // tslint:disable-next-line: max-line-length + this.notification.newNotification({ user: 'Download manager', message: `The song ${res.data.result.title} is downloaded successfully.`}, 100000); + this.downloadQueue.splice(index, 1); + }); + } + + getDownloadQueue() { + return this.data.downloadqueue(); + } + + // Input like: "00:00:54.43" + convertTimeToSeconds(input: string): number { + if (typeof input === 'string') { + const parts = input.split(/[\:\.]/); + let total = 0; + + total += Number(parts[0]) * 60 * 60; // Hours + total += Number(parts[1]) * 60; // Minutes + total += Number(parts[2]); // Seconds + total += Number(parts[3]) / 100; // Centiseconds + + return total; + } else { + return; + } + } +} diff --git a/src/app/services/notification.service.ts b/src/app/services/notification.service.ts index add74f5..304dc08 100644 --- a/src/app/services/notification.service.ts +++ b/src/app/services/notification.service.ts @@ -1,9 +1,24 @@ import { Injectable } from '@angular/core'; +interface Notification { + user: string; + message: string; +} + @Injectable({ providedIn: 'root' }) export class NotificationService { - constructor() { } + constructor() { + + } + + notificationStack: Notification[] = []; + + newNotification(notification: Notification, timeout: number = 3000) { + const index = this.notificationStack.push(notification); + setTimeout(() => this.notificationStack.splice(index - 1, 1), timeout); + } + }