diff --git a/package-lock.json b/package-lock.json index c50f656..72f62e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -566,6 +566,14 @@ "semver-intersect": "1.4.0" } }, + "@types/axios": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@types/axios/-/axios-0.14.0.tgz", + "integrity": "sha1-7CMA++fX3d1+udOr+HmZlkyvzkY=", + "requires": { + "axios": "*" + } + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -1214,6 +1222,38 @@ "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", "dev": true }, + "axios": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz", + "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==", + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + } + }, + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==" + } + } + }, "axobject-query": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.0.2.tgz", @@ -6034,8 +6074,7 @@ "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "multicast-dns": { "version": "6.2.3", diff --git a/package.json b/package.json index c41add2..4aca523 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,8 @@ "@fortawesome/angular-fontawesome": "^0.5.0", "@fortawesome/fontawesome-svg-core": "^1.2.24", "@fortawesome/free-solid-svg-icons": "^5.11.1", + "@types/axios": "^0.14.0", + "axios": "^0.19.0", "rxjs": "~6.4.0", "tslib": "^1.9.0", "zone.js": "~0.9.1" @@ -31,9 +33,9 @@ "@angular/cli": "~8.0.6", "@angular/compiler-cli": "~8.0.3", "@angular/language-service": "~8.0.3", - "@types/node": "~8.9.4", "@types/jasmine": "~3.3.8", "@types/jasminewd2": "~2.0.3", + "@types/node": "^8.9.5", "codelyzer": "^5.0.0", "jasmine-core": "~3.4.0", "jasmine-spec-reporter": "~4.2.1", diff --git a/src/app/classes/album.spec.ts b/src/app/classes/album.spec.ts deleted file mode 100644 index 5456017..0000000 --- a/src/app/classes/album.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Album } from './album'; - -describe('Album', () => { - it('should create an instance', () => { - expect(new Album()).toBeTruthy(); - }); -}); diff --git a/src/app/classes/album.ts b/src/app/classes/album.ts deleted file mode 100644 index c272f54..0000000 --- a/src/app/classes/album.ts +++ /dev/null @@ -1,2 +0,0 @@ -export class Album { -} diff --git a/src/app/classes/artist.spec.ts b/src/app/classes/artist.spec.ts deleted file mode 100644 index 5c78ac3..0000000 --- a/src/app/classes/artist.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Artist } from './artist'; - -describe('Artist', () => { - it('should create an instance', () => { - expect(new Artist()).toBeTruthy(); - }); -}); diff --git a/src/app/classes/artist.ts b/src/app/classes/artist.ts deleted file mode 100644 index 1060425..0000000 --- a/src/app/classes/artist.ts +++ /dev/null @@ -1,2 +0,0 @@ -export class Artist { -} diff --git a/src/app/classes/entities.ts b/src/app/classes/entities.ts new file mode 100644 index 0000000..72c7727 --- /dev/null +++ b/src/app/classes/entities.ts @@ -0,0 +1,74 @@ +import { DataService } from '../services/data.service'; + +/** This class represents one song. */ +export class Song { + + // The following list are identical to the items returned from the api. + id: number; + name: string; + artist: number; + album: number; + path: string; + + data: DataService; + + constructor(data: any) { + this.id = data.data.result[0].id; + this.name = data.data.result[0].name; + this.artist = data.data.result[0].artist; + this.album = data.data.result[0].album; + this.path = data.data.result[0].path; + + this.data = new DataService(); + } + + async getArtist() { + // tslint:disable-next-line: no-use-before-declare + return new Artist(await this.data.getArtist(this.artist)); + } +} + +/** This class represents one artist */ +export class Artist { + + // The following list are identical to the items returned from the api. + id: number; + name: string; + albums: Array; + songs: Array; + + constructor(data: any) { + this.id = data.data.result[0].id; + this.name = data.data.result[0].name; + this.albums = data.data.result[0].album; + this.songs = data.data.result[0].song; + } + + // There will be more functions here... +} + +/** This class represents one album */ +export class Album { + + // The following list are identical to the items returned from the api. + id: number; + name: string; + artist: number; + image: string; + + data: DataService; + + constructor(data: any) { + this.id = data.data.result[0].id; + this.name = data.data.result[0].name; + this.artist = data.data.result[0].artist; + this.image = data.data.result[0].image; + } + + async getArtist() { + return new Artist(await this.data.getArtist(this.artist)); + } + + // There will be more functions here... + +} diff --git a/src/app/classes/song.spec.ts b/src/app/classes/song.spec.ts deleted file mode 100644 index 99612bb..0000000 --- a/src/app/classes/song.spec.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Song } from './song'; - -describe('Song', () => { - it('should create an instance', () => { - expect(new Song()).toBeTruthy(); - }); -}); diff --git a/src/app/classes/song.ts b/src/app/classes/song.ts deleted file mode 100644 index d87a3f3..0000000 --- a/src/app/classes/song.ts +++ /dev/null @@ -1,2 +0,0 @@ -export class Song { -} diff --git a/src/app/components/artists/artists.component.html b/src/app/components/artists/artists.component.html index 3283436..f550799 100644 --- a/src/app/components/artists/artists.component.html +++ b/src/app/components/artists/artists.component.html @@ -1,7 +1,7 @@ @@ -12,22 +12,24 @@
-

{{ data.result[0].name }}'s profile

+

{{ data.name }}'s profile

-

{{ data.result[0].name }}'s albums

+

{{ data.name }}'s albums

    -
  • - {{ album.name }} +
  • + {{ album }}
-

{{ data.result[0].name }}'s songs

+

{{ data.name }}'s songs

    -
  • - {{ song.name }} +
  • + {{ song }}
+ {{ data | json}} +
diff --git a/src/app/components/artists/artists.component.ts b/src/app/components/artists/artists.component.ts index 8b04530..513c49b 100644 --- a/src/app/components/artists/artists.component.ts +++ b/src/app/components/artists/artists.component.ts @@ -1,6 +1,6 @@ import { Component, OnInit } from '@angular/core'; -import { DataService } from 'src/app/services/data.service'; import { ActivatedRoute } from '@angular/router'; +import { ApiService } from 'src/app/services/api.service'; @Component({ selector: 'app-artists', @@ -13,7 +13,7 @@ export class ArtistsComponent implements OnInit { artists; constructor( - private data: DataService, + private api: ApiService, private route: ActivatedRoute ) { } @@ -25,12 +25,12 @@ export class ArtistsComponent implements OnInit { if (params.has('id')) { // ... then get artist data ... - this.artistData = this.data.getArtist(params.get('id')); + this.artistData = this.api.getArtist(Number(params.get('id'))); } else { // Otherwise, get all artists and list them out. - this.artists = this.data.getAllArtists(); + this.artists = this.api.getAllArtists(); } diff --git a/src/app/components/controls/controls.component.html b/src/app/components/controls/controls.component.html index 57f9fb0..75d3615 100644 --- a/src/app/components/controls/controls.component.html +++ b/src/app/components/controls/controls.component.html @@ -1,7 +1,7 @@
- {{ song.result[0].name }} + {{ song.name }}
diff --git a/src/app/components/controls/controls.component.ts b/src/app/components/controls/controls.component.ts index bb96977..c2d2735 100644 --- a/src/app/components/controls/controls.component.ts +++ b/src/app/components/controls/controls.component.ts @@ -1,8 +1,8 @@ import { Component, OnInit } from '@angular/core'; import { faPlay, faPause } from '@fortawesome/free-solid-svg-icons'; import { AudioService } from 'src/app/services/audio.service'; -import { DataService } from 'src/app/services/data.service'; -import { Observable } from 'rxjs'; +import { Song } from '../../classes/entities'; +import { ApiService } from 'src/app/services/api.service'; @Component({ selector: 'app-controls', @@ -15,16 +15,16 @@ export class ControlsComponent implements OnInit { faPlay = faPlay; faPause = faPause; - currentSong: Observable; + currentSong: Promise; constructor( public audio: AudioService, - private data: DataService + private api: ApiService ) { } ngOnInit() { this.audio.currentSong.subscribe(id => { - this.currentSong = this.data.getSong(id); + this.currentSong = this.api.getSong(id); }); } diff --git a/src/app/components/home/home.component.html b/src/app/components/home/home.component.html index d0fb6cb..3f0b9dc 100644 --- a/src/app/components/home/home.component.html +++ b/src/app/components/home/home.component.html @@ -1,3 +1,3 @@ -

- {{ song.result[0].name }} -

+
+

{{ song.name }} by {{ artists[i].name }}

+
diff --git a/src/app/components/home/home.component.ts b/src/app/components/home/home.component.ts index 21233e7..3dafe4e 100644 --- a/src/app/components/home/home.component.ts +++ b/src/app/components/home/home.component.ts @@ -1,7 +1,7 @@ import { Component, OnInit } from '@angular/core'; -import { DataService } from 'src/app/services/data.service'; -import { Observable } from 'rxjs'; import { AudioService } from 'src/app/services/audio.service'; +import { Song, Artist } from 'src/app/classes/entities'; +import { ApiService } from 'src/app/services/api.service'; @Component({ selector: 'app-home', @@ -10,16 +10,23 @@ import { AudioService } from 'src/app/services/audio.service'; }) export class HomeComponent implements OnInit { - song: Observable; + songs: Song[]; + artists: Artist[] = []; constructor( - private data: DataService, + private api: ApiService, public audio: AudioService ) { } ngOnInit() { // Load the first song from the API - this.song = this.data.getSong(0); + // Removing the async / await from the .then() callback will load the artists async from the songs. + this.api.getSong('all').then(async res => { + this.songs = res; + await res.forEach(async (song: Song) => { + this.artists.push(await song.getArtist()); + }); + }); } } diff --git a/src/app/services/api.service.spec.ts b/src/app/services/api.service.spec.ts new file mode 100644 index 0000000..9eb2c83 --- /dev/null +++ b/src/app/services/api.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { ApiService } from './api.service'; + +describe('ApiService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: ApiService = TestBed.get(ApiService); + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/services/api.service.ts b/src/app/services/api.service.ts new file mode 100644 index 0000000..5d469ff --- /dev/null +++ b/src/app/services/api.service.ts @@ -0,0 +1,73 @@ +import { Injectable } from '@angular/core'; +import { DataService } from './data.service'; +import { EntityService } from './entity.service'; + +/** This class contains helper functions to retreive data from the api. */ +@Injectable({ + providedIn: 'root' +}) +export class ApiService { + + constructor( + private data: DataService, + private entityService: EntityService + ) { } + + /** + * This function gets one or multiple songs from the api's database. + * @param id The id of the song. Either a number, a string with numbers by commas or the string 'all'. + * @returns a promise. When resolved it contains either one Song entity or an array of Song entities. + */ + async getSong(id: number | string): Promise { + // Retreive data from the api... + const data = await this.data.getSong(id); + // ... and convert that data to entities. + return this.entityService.createEntityFromData(data); + } + + /** + * This function gets one or multiple artists from the api's database. + * @param id The id of the song. Either a number, a string with numbers by commas or the string 'all'. + * @returns a promise. When resolved it contains either one Artist entity or an array of Artist entities. + */ + async getArtist(id: number | string): Promise { + // Retreive data from the api... + const data = await this.data.getArtist(id); + // ... and convert that data to entities. + return this.entityService.createEntityFromData(data); + } + + /** + * This function gets one or multiple albums from the api's database. + * @param id The id of the song. Either a number, a string with numbers by commas or the string 'all'. + * @returns a promise. When resolved it contains either one Album entity or an array of Album entities. + */ + async getAlbum(id: number | string): Promise { + // Retreive data from the api... + const data = await this.data.getAlbum(id); + // ... and convert that data to entities. + return this.entityService.createEntityFromData(data); + } + + /** + * This function gets all albums from the api's database. + * @returns a promise. When resolved it contains either one Album entity or an array of Album entities. + */ + async getAllAlbums(): Promise { + // Retreive data from the api... + const data = await this.data.getAllAlbums(); + // ... and convert that data to entities. + return this.entityService.createEntityFromData(data); + } + + /** + * This function gets all albums from the api's database. + * @returns a promise. When resolved it contains either one Album entity or an array of Album entities. + */ + async getAllArtists(): Promise { + // Retreive data from the api... + const data = await this.data.getAllArtists(); + // ... and convert that data to entities. + return this.entityService.createEntityFromData(data); + } +} diff --git a/src/app/services/audio.service.ts b/src/app/services/audio.service.ts index 49bd2b0..85835d3 100644 --- a/src/app/services/audio.service.ts +++ b/src/app/services/audio.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@angular/core'; import { Subject } from 'rxjs'; import { DataService } from './data.service'; +import { Song } from '../classes/entities'; /** * The audio service handles all logic that has to do with the audio player. @@ -41,11 +42,8 @@ export class AudioService { * The setSong function set the song for the player. * @param id The id of the song that needs to be played */ - setSong(id: number) { - this.data.getSong(id).subscribe((res: any) => { - console.log(this.data.apiUrl + '/play/' + res.result[0].path); - this.player.src = this.data.apiUrl + '/play/' + res.result[0].path; - this.currentSong.next(id); - }); + setSong(song: Song) { + this.player.src = this.data.apiUrl + '/play/' + song.path; + this.currentSong.next(song.id); } } diff --git a/src/app/services/data.service.ts b/src/app/services/data.service.ts index 02d1bcb..661fb05 100644 --- a/src/app/services/data.service.ts +++ b/src/app/services/data.service.ts @@ -1,6 +1,10 @@ import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { Observable } from 'rxjs'; +import axios, { AxiosResponse } from 'axios'; +import { AxiosInstance } from 'axios'; + +export interface ApiData { + result: any; +} /** * The DataService handles all data traffic to and from the API. @@ -13,56 +17,64 @@ import { Observable } from 'rxjs'; }) export class DataService { + // This variable could even be moved elsewhere. public apiUrl = 'http://localhost:673'; - constructor(private http: HttpClient) { } + /** This variable will store the axios http client. */ + private axiosInstance: AxiosInstance; + + constructor() { + this.axiosInstance = axios.create({ + timeout: 3000 + }); + } /** * @param type The type of the thing you want to get. The type is a string containing either 'artist', 'album' or 'song'. * @param id The id of the artist. It can be a number representing the id, multiple numbers seperated by commas or the string 'all'. - * @returns An Observable containing the returned data from the API. + * @returns A Promise> containing the returned data from the API. */ - get(type: string, id: string | number): Observable { - return this.http.get(`${this.apiUrl}/get/${type}/${id}`); + get(type: string, id: string | number): Promise> { + return this.axiosInstance.get(`${this.apiUrl}/get/${type}/${id}`); } /** * @param id The id of the artist. It can be a number representing the id, multiple numbers seperated by commas or the string 'all'. - * @returns An Observable containing the returned data from the API. + * @returns A Promise> containing the returned data from the API. */ - getSong(id: number | string): Observable { + getSong(id: number | string): Promise> { return this.get('song', id); } /** * @param id The id of the artist. It can be a number representing the id, multiple numbers seperated by commas or the string 'all'. - * @returns An Observable containing the returned data from the API. + * @returns A Promise> containing the returned data from the API. */ - getAlbum(id: number | string): Observable { + getAlbum(id: number | string): Promise> { return this.get('album', id); } /** * A function to get one, multiple or all artists. * @param id The id of the artist. It can be a number representing the id, multiple numbers seperated by commas or the string 'all'. - * @returns An Observable containing the returned data from the API. + * @returns A Promise> containing the returned data from the API. */ - getArtist(id: number | string): Observable { - return this.get('artist', id); + getArtist(id: number | string): Promise> { + return this.get('artist', id); } /** * A function to get all albums from the API. - * @returns An Observable containing the returned data from the API. + * @returns A Promise> containing the returned data from the API. */ - getAllAlbums(): Observable { + getAllAlbums(): Promise> { return this.get('album', 'all'); } /** * A function to get all artists from the API. - * @returns An Observable containing the returned data from the API. + * @returns A Promise> containing the returned data from the API. */ - getAllArtists(): Observable { + getAllArtists(): Promise> { return this.get('artist', 'all'); } } diff --git a/src/app/services/entity.service.spec.ts b/src/app/services/entity.service.spec.ts new file mode 100644 index 0000000..cbf7d19 --- /dev/null +++ b/src/app/services/entity.service.spec.ts @@ -0,0 +1,12 @@ +import { TestBed } from '@angular/core/testing'; + +import { EntityService } from './entity.service'; + +describe('EntityService', () => { + beforeEach(() => TestBed.configureTestingModule({})); + + it('should be created', () => { + const service: EntityService = TestBed.get(EntityService); + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/services/entity.service.ts b/src/app/services/entity.service.ts new file mode 100644 index 0000000..1983fbb --- /dev/null +++ b/src/app/services/entity.service.ts @@ -0,0 +1,57 @@ +import { Injectable } from '@angular/core'; +import { Song, Album, Artist } from '../classes/entities'; + +@Injectable({ + providedIn: 'root' +}) +export class EntityService { + + constructor() { } + + /** + * This function takes api data and converts it into entities. + * @param data The data from the api. + * @returns either an array of entities or a single entity. + */ + createEntityFromData(data: any) { + // If data child is present... + if (data.data) { + + // Prepare array for elements to be returned. + const result = []; + + // For every entity in the api data. + data.data.result.forEach(element => { + + // If the type is... + switch (element.type) { + // ... a song, add a song entity to the result array. + case 'song': + result.push(new Song({data: {result: [element]}})); + break; + + // ... an artist, add an artist entity to the result array. + case 'artist': + result.push(new Artist({ data: { result: [element] } })); + break; + + // ... an album, add an album entity to the result array. + case 'album': + result.push(new Album({ data: { result: [element] } })); + break; + + // .. anything else, just ignore it. + default: + break; + } + + }); // end result.forEach + + // Return the result array, if it has more than one item. + return result.length > 1 ? result : result[0]; + + } else { + // ? Make sure that the data is in the correct format. + } + } +}