Album + slow service + other
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
POST http://localhost:9200/itunessongs/_search
|
||||
POST http://localhost:9200/itunessongs/album/_search
|
||||
content-type: application/json
|
||||
User-Agent: vscode-restclient
|
||||
|
||||
@@ -6,7 +6,7 @@ User-Agent: vscode-restclient
|
||||
"query": {
|
||||
"match" : {
|
||||
"Album" : {
|
||||
"query": "Our Love To Admire",
|
||||
"query": "Crystal Castles",
|
||||
"operator" : "and"
|
||||
}
|
||||
}
|
||||
@@ -14,20 +14,20 @@ User-Agent: vscode-restclient
|
||||
"size": 20
|
||||
}
|
||||
|
||||
POST http://localhost:9200/itunessongs/_search
|
||||
POST http://localhost:9200/itunessongs/song/_search
|
||||
content-type: application/json
|
||||
User-Agent: vscode-restclient
|
||||
|
||||
{
|
||||
"query": {
|
||||
"match_phrase" : {
|
||||
"Album" : "Our Love"
|
||||
"Album" : "Crystal Castles"
|
||||
}
|
||||
},
|
||||
"size": 20
|
||||
}
|
||||
|
||||
POST http://localhost:9200/itunessongs/_search
|
||||
POST http://localhost:9200/itunessongs/song/_search
|
||||
content-type: application/json
|
||||
User-Agent: vscode-restclient
|
||||
|
||||
@@ -35,8 +35,27 @@ User-Agent: vscode-restclient
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": [
|
||||
{"match_phrase" : { "Album" : "Our Love" }},
|
||||
{"match_phrase" : { "Artist" : "Caribou." }}
|
||||
{"match_phrase" : { "Album" : "Crystal Castles" }},
|
||||
{"match_phrase" : { "Artist" : "Crystal Castles" }}
|
||||
]
|
||||
}
|
||||
},
|
||||
"size": 20
|
||||
}
|
||||
|
||||
POST http://localhost:9200/itunessongs/song/_search
|
||||
content-type: application/json
|
||||
User-Agent: vscode-restclient
|
||||
|
||||
{
|
||||
"query": {
|
||||
"bool": {
|
||||
"must": {
|
||||
"match_phrase" : { "Album" : "Crystal Castles" }
|
||||
},
|
||||
"should" : [
|
||||
{"match_phrase" : { "Artist" : "Crystal Castles" }},
|
||||
{"match_phrase" : { "Album Artist" : "Crystal Castles" }}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,7 +1,38 @@
|
||||
<h1>{{albumName}}</h1>
|
||||
|
||||
<ul class="heroes">
|
||||
<div class="well">
|
||||
<h3>Debug Zone</h3>
|
||||
Returned song: {{songs.length}}<br />
|
||||
Theorical number song: {{ album ? album['Track Count'] : "" }}
|
||||
<span *ngIf="album && (songs.length == album['Track Count'])" class="glyphicon glyphicon-ok" style="color:green"></span>
|
||||
<span *ngIf="album && (songs.length != album['Track Count'])" class="glyphicon glyphicon-remove" style="color:red"></span>
|
||||
</div>
|
||||
|
||||
<!--<ul class="heroes">
|
||||
<li *ngFor="let song of songs | sortBy : 'Track Number'" >
|
||||
{{("0" + song['Track Number']).slice(-2)}}. {{song.Name}} ({{song.Artist}})
|
||||
{{("0" + song['Track Number']).slice(-2)}}. {{song.Name}} ({{song.Artist}}) [{{song.Album}}]
|
||||
</li>
|
||||
</ul>
|
||||
</ul>-->
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Name</th>
|
||||
<th>Artist</th>
|
||||
<th>Album</th>
|
||||
<th>Album Artist</th>
|
||||
<th>Play Count</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let song of songs | sortBy : 'Track Number'">
|
||||
<td>{{song['Track Number'] ? (("0" + song['Track Number']).slice(-2)) : "--"}}</td>
|
||||
<td [title]="song.Name">{{song.Name}}</td>
|
||||
<td [title]="song.Artist"><a [routerLink]="['/artist', song.Artist]">{{song.Artist}}</a></td>
|
||||
<td [title]="song.Album"><a [routerLink]="['/album', song.Album]">{{song.Album}}</a></td>
|
||||
<td [title]="song.Album">{{song['Album Artist']}}</td>
|
||||
<td [title]="song['Play Count']">{{song['Play Count']}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -4,6 +4,7 @@ import { Location } from '@angular/common'
|
||||
|
||||
import { ElsService } from './els.service'
|
||||
import { Song } from './object/song';
|
||||
import { Album } from './object/album';
|
||||
|
||||
@Component({
|
||||
selector: 'album-component',
|
||||
@@ -22,14 +23,17 @@ export class AlbumComponent implements OnInit {
|
||||
|
||||
songs: Array<Song> = [];
|
||||
|
||||
album: Album = new Album(); // If album not found, will be replaced by 'undefined'
|
||||
|
||||
ngOnInit(): void {
|
||||
this.route.params
|
||||
.subscribe((params: Params) => this.albumName = params['name']);
|
||||
|
||||
this.elsService.getAlbumSongs(this.albumName).subscribe(
|
||||
data => {
|
||||
this.songs = data;
|
||||
}
|
||||
);
|
||||
this.elsService.getAlbum(this.albumName).subscribe(data => {
|
||||
this.album = data;
|
||||
console.log(this.album);
|
||||
});
|
||||
|
||||
this.elsService.getAlbumSongs(this.albumName).subscribe(data => this.songs = data);
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,10 @@
|
||||
<span class="glyphicon glyphicon-time stats_icon"></span>
|
||||
</div>
|
||||
<div class="col-xs-9 text-right">
|
||||
<div class="huge"><h3>{{totalTimeSt}}</h3></div>
|
||||
<div>
|
||||
<h3 *ngIf="!totalTimeSt"><span class="glyphicon glyphicon-refresh loading"></span></h3>
|
||||
<h3 *ngIf="totalTimeSt">{{totalTimeSt}}</h3>
|
||||
</div>
|
||||
<div><br>Total time</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -28,7 +31,10 @@
|
||||
<span class="glyphicon glyphicon-hdd stats_icon"></span>
|
||||
</div>
|
||||
<div class="col-xs-9 text-right">
|
||||
<div class="huge"><h3>{{totalSizeSt}}</h3></div>
|
||||
<div>
|
||||
<h3 *ngIf="!totalSizeSt"><span class="glyphicon glyphicon-refresh loading"></span></h3>
|
||||
<h3 *ngIf="totalSizeSt">{{totalSizeSt}}</h3>
|
||||
</div>
|
||||
<div><br>Total size</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,7 +49,10 @@
|
||||
<span class="glyphicon glyphicon-play stats_icon"></span>
|
||||
</div>
|
||||
<div class="col-xs-9 text-right">
|
||||
<div class="huge"><h3>{{trackCountSong}}</h3></div>
|
||||
<div>
|
||||
<h3 *ngIf="!trackCountSong"><span class="glyphicon glyphicon-refresh loading"></span></h3>
|
||||
<h3 *ngIf="trackCountSong">{{trackCountSong}}</h3>
|
||||
</div>
|
||||
<div><br>Total Songs</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -54,21 +63,21 @@
|
||||
|
||||
<h3>Top Played Songs</h3>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Track Name</th>
|
||||
<th>Artist</th>
|
||||
<th>Album</th>
|
||||
<th>Play Count</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let song of mostPlayedSongs">
|
||||
<td [title]="song.Name">{{song.Name}}</td>
|
||||
<td [title]="song.Artist"><a [routerLink]="['/artist', song.Artist]">{{song.Artist}}</a></td>
|
||||
<td [title]="song.Album"><a [routerLink]="['/album', song.Album]">{{song.Album}}</a></td>
|
||||
<td [title]="song['Play Count']">{{song['Play Count']}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Track Name</th>
|
||||
<th>Artist</th>
|
||||
<th>Album</th>
|
||||
<th>Play Count</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let song of mostPlayedSongs">
|
||||
<td [title]="song.Name">{{song.Name}}</td>
|
||||
<td [title]="song.Artist"><a [routerLink]="['/artist', song.Artist]">{{song.Artist}}</a></td>
|
||||
<td [title]="song.Album"><a [routerLink]="['/album', song.Album]">{{song.Album}}</a></td>
|
||||
<td [title]="song['Play Count']">{{song['Play Count']}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -35,11 +35,11 @@ export class DashboardComponent implements OnInit {
|
||||
this.totalSizeSt = this.convertSizeToString(result);
|
||||
});
|
||||
|
||||
this.elsService.getTrackCount("song")
|
||||
this.elsService.getCount("song")
|
||||
.then(result => this.trackCountSong = result);
|
||||
this.elsService.getTrackCount("artist")
|
||||
this.elsService.getCount("artist")
|
||||
.then(result => this.trackCountArtist = result);
|
||||
this.elsService.getTrackCount("album")
|
||||
this.elsService.getCount("album")
|
||||
.then(result => this.trackCountAlbum = result);
|
||||
|
||||
this.elsService.getMostPlayedTrack().subscribe(
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Headers, Http } from '@angular/http';
|
||||
|
||||
import { Song } from './object/song';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/toPromise';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
import { Song } from './object/song';
|
||||
import { Album } from './object/album';
|
||||
|
||||
@Injectable()
|
||||
export class ElsService {
|
||||
private elsUrl = 'http://localhost:9200/itunessongs/';
|
||||
@@ -22,6 +22,12 @@ export class ElsService {
|
||||
.catch(this.handleError);
|
||||
}
|
||||
|
||||
getTimeSlowly(): Promise<number> {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => resolve(this.getTime()), 2000);
|
||||
});
|
||||
}
|
||||
|
||||
getSize(): Promise<number> {
|
||||
return this.http.post(this.elsUrl + "_search", JSON.stringify({aggs:{sum_time:{sum:{field:"Size"}}},"size":0}), {headers: this.headers})
|
||||
.toPromise()
|
||||
@@ -29,15 +35,28 @@ export class ElsService {
|
||||
.catch(this.handleError);
|
||||
}
|
||||
|
||||
getTrackCount(type: string): Promise<number> {
|
||||
getSizeSlowly(): Promise<number> {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => resolve(this.getSize()), 2000);
|
||||
});
|
||||
}
|
||||
|
||||
getCount(type: string): Promise<number> {
|
||||
return this.http.get(this.elsUrl + type + "/_count")
|
||||
.toPromise()
|
||||
.then(res => res.json().count as number)
|
||||
.catch(this.handleError);
|
||||
}
|
||||
|
||||
getTrackCountSlowly(type: string): Promise<number> {
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => resolve(this.getCount(type)), 2000);
|
||||
});
|
||||
}
|
||||
|
||||
getMostPlayedTrack(): Observable<Song[]> {
|
||||
// Thank to http://chariotsolutions.com/blog/post/angular2-observables-http-separating-services-components/
|
||||
// for the map part
|
||||
|
||||
// Could be shorter but I think it's more readable like this.
|
||||
return this.http
|
||||
@@ -67,7 +86,7 @@ export class ElsService {
|
||||
getAlbumSongs(albumName: string): Observable<Song[]> {
|
||||
return this.http
|
||||
.post(this.elsUrl + "song/_search",
|
||||
JSON.stringify({"query":{"match_phrase":{"Album":albumName}},"size": 20}), // TODO Dynamic size
|
||||
JSON.stringify({"query":{"match_phrase":{"Album":albumName}},"size": 50}), // TODO Dynamic size
|
||||
{headers: this.headers})
|
||||
.map(res => {
|
||||
return res.json().hits.hits;
|
||||
@@ -81,6 +100,26 @@ export class ElsService {
|
||||
});
|
||||
}
|
||||
|
||||
getAlbum(albumName: string): Observable<Album> {
|
||||
return this.http
|
||||
.post(this.elsUrl + "album/_search",
|
||||
JSON.stringify({"query":{"match_phrase":{"Album":albumName}},"size": 50}), // TODO Dynamic size
|
||||
{headers: this.headers})
|
||||
.map(res => {
|
||||
return res.json().hits.hits;
|
||||
})
|
||||
.map((hits: Array<any>) => {
|
||||
if (hits.length < 1) {
|
||||
console.info('No album "' + albumName + '" found.');
|
||||
return undefined;
|
||||
}
|
||||
if (hits.length > 1) {
|
||||
console.error('More than one album "' + albumName + '" found, return the first.');
|
||||
}
|
||||
return hits[0]._source;
|
||||
});
|
||||
}
|
||||
|
||||
private handleError(error: any): Promise<any> {
|
||||
console.error('An error occurred', error); // for demo purposes only
|
||||
return Promise.reject(error.message || error);
|
||||
|
||||
12
dashboard/src/app/object/album.ts
Normal file
12
dashboard/src/app/object/album.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
export class Album {
|
||||
Name: string;
|
||||
Album: string;
|
||||
Artist: Array<string>;
|
||||
Rating: number;
|
||||
Genre: Array<string>;
|
||||
"Track Count": number;
|
||||
"Album Rating": number;
|
||||
"Persistent ID": string;
|
||||
"Album Rating Computed": boolean;
|
||||
"Play Count": number;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Angular QuickStart</title>
|
||||
<title>Music Dashboard</title>
|
||||
<base href="/">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
@@ -13,6 +13,10 @@
|
||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css">
|
||||
<script src="node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('main.js').catch(function(err){ console.error(err); });
|
||||
|
||||
@@ -20,6 +20,17 @@ body, input[text], button {
|
||||
font-family: Cambria, Georgia;
|
||||
}
|
||||
|
||||
.loading {
|
||||
-animation: spin 1.5s infinite linear;
|
||||
-webkit-animation: spin2 1.5s infinite linear;
|
||||
}
|
||||
|
||||
@-webkit-keyframes spin2 {
|
||||
from { -webkit-transform: rotate(0deg);}
|
||||
to { -webkit-transform: rotate(360deg);}
|
||||
}
|
||||
|
||||
|
||||
/* . . . */
|
||||
/* everywhere else */
|
||||
* {
|
||||
|
||||
Reference in New Issue
Block a user