add jukebox
This commit is contained in:
parent
49e695b67b
commit
5350e45ce6
@ -32,10 +32,13 @@ import { BorrowItemEditComponent, BorrowItemsComponent } from './pages/borrow/it
|
|||||||
import { BorrowRequestsComponent } from './pages/borrow/requests/requests.component';
|
import { BorrowRequestsComponent } from './pages/borrow/requests/requests.component';
|
||||||
import { BorrowComponent } from './pages/borrow/borrow.component';
|
import { BorrowComponent } from './pages/borrow/borrow.component';
|
||||||
import { InviteCodeComponent } from './pages/invites/code/code.component';
|
import { InviteCodeComponent } from './pages/invites/code/code.component';
|
||||||
|
import { JukeboxComponent } from './pages/jukebox/jukebox.compontent';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{ path: 'profile/:username', component: UserComponent, canActivate: [ AuthUpdateGuard ] },
|
{ path: 'profile/:username', component: UserComponent, canActivate: [ AuthUpdateGuard ] },
|
||||||
{ path: 'edit-profile', component: ProfileComponent, canActivate: [ AuthenticatedGuard ] },
|
{ path: 'edit-profile', component: ProfileComponent, canActivate: [ AuthenticatedGuard ] },
|
||||||
|
{ path: 'partey/jukebox', component: JukeboxComponent, canActivate: [ AuthenticatedGuard ] },
|
||||||
|
{ path: 'partey/manage', component: ParteyComponent, canActivate: [ AuthenticatedGuard ] },
|
||||||
{
|
{
|
||||||
path: '', component: MainComponent, children: [
|
path: '', component: MainComponent, children: [
|
||||||
{ path: '', redirectTo: "/services", pathMatch: 'full' },
|
{ path: '', redirectTo: "/services", pathMatch: 'full' },
|
||||||
|
@ -65,6 +65,7 @@ import { MatPaginatorIntl } from '@angular/material/paginator';
|
|||||||
import { NgxMatDateAdapter, NGX_MAT_DATE_FORMATS } from '@angular-material-components/datetime-picker';
|
import { NgxMatDateAdapter, NGX_MAT_DATE_FORMATS } from '@angular-material-components/datetime-picker';
|
||||||
import { MAT_DATE_LOCALE } from '@angular/material/core';
|
import { MAT_DATE_LOCALE } from '@angular/material/core';
|
||||||
import { NgxMatMomentAdapter } from '@angular-material-components/moment-adapter';
|
import { NgxMatMomentAdapter } from '@angular-material-components/moment-adapter';
|
||||||
|
import { JukeboxComponent } from './pages/jukebox/jukebox.compontent';
|
||||||
|
|
||||||
|
|
||||||
export function init_app(i18n: I18nService) {
|
export function init_app(i18n: I18nService) {
|
||||||
@ -124,7 +125,8 @@ export class XhrInterceptor implements HttpInterceptor {
|
|||||||
UrlShortenerComponent, UrlShortenerShareDialog, UrlShortenerEditDialog, UrlShortenerPasswordComponent,
|
UrlShortenerComponent, UrlShortenerShareDialog, UrlShortenerEditDialog, UrlShortenerPasswordComponent,
|
||||||
BorrowComponent, BorrowItemsComponent, BorrowItemEditComponent, BorrowRequestsComponent, BorrowRequestEditComponent, BorrowProvingComponent, BorrowProvingResultDialog,
|
BorrowComponent, BorrowItemsComponent, BorrowItemEditComponent, BorrowRequestsComponent, BorrowRequestEditComponent, BorrowProvingComponent, BorrowProvingResultDialog,
|
||||||
DividerComponent, DividertestComponent,
|
DividerComponent, DividertestComponent,
|
||||||
DurationpickerComponent
|
DurationpickerComponent,
|
||||||
|
JukeboxComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
75
src/app/pages/jukebox/jukebox.component.html
Normal file
75
src/app/pages/jukebox/jukebox.component.html
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<div *ngIf="active">
|
||||||
|
<mat-card>
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>{{'jukebox' | i18n}}</mat-card-title>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<form (submit)="search()">
|
||||||
|
<mat-form-field>
|
||||||
|
<input matInput [formControl]="searchFormControl" placeholder="{{'jukebox.search' | i18n}}">
|
||||||
|
</mat-form-field>
|
||||||
|
</form>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
<button mat-raised-button type="submit" [disabled]="!searchFormControl.value || timeout > 0 || searchDisabled">
|
||||||
|
<mat-icon inline="true">{{'jukebox.search.icon' | i18n}}</mat-icon>
|
||||||
|
{{'jukebox.search.submit' | i18n}}
|
||||||
|
</button>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<p *ngIf="timeout">{{'jukebox.timeout' | i18n:timeout}}</p>
|
||||||
|
|
||||||
|
<div *ngIf="searchResult && searchResult.items">
|
||||||
|
<mat-card class="track" *ngFor="let track of searchResult.items" (click)="queue(track)">
|
||||||
|
<mat-card-title-group>
|
||||||
|
<img *ngIf="getImageUrl(track)" mat-card-sm-image [src]="getImageUrl(track)">
|
||||||
|
<mat-card-title>{{track.name}}</mat-card-title>
|
||||||
|
<mat-card-subtitle>
|
||||||
|
<span *ngFor="let artist of track.artists; let i = index">
|
||||||
|
{{artist.name}}<span *ngIf="(i+1) < track.artists.length">, </span>
|
||||||
|
</span>
|
||||||
|
</mat-card-subtitle>
|
||||||
|
</mat-card-title-group>
|
||||||
|
</mat-card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button *ngIf="searchResult && ((searchResult.offset + searchResult.limit) < searchResult.total)" mat-button
|
||||||
|
(click)="searchMore()"> {{'jukebox.search.more' | i18n}}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<mat-card class="accent" *ngIf="wait">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>{{'jukebox' | i18n}}</mat-card-title>
|
||||||
|
<mat-card-subtitle>{{'jukebox.wait' | i18n}}</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<p *ngIf="timeout">{{'jukebox.timeout' | i18n:timeout}}</p>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<mat-card class="warn" *ngIf="unavailable">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>{{'jukebox' | i18n}}</mat-card-title>
|
||||||
|
<mat-card-subtitle>{{'jukebox.unavailable' | i18n}}</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<p>{{'jukebox.unavailable.hint' | i18n}}</p>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
||||||
|
|
||||||
|
<mat-card *ngIf="forbidden">
|
||||||
|
<mat-card-header>
|
||||||
|
<mat-card-title>{{'jukebox' | i18n}}</mat-card-title>
|
||||||
|
<mat-card-subtitle>{{'jukebox.forbidden' | i18n}}</mat-card-subtitle>
|
||||||
|
</mat-card-header>
|
||||||
|
<mat-card-content>
|
||||||
|
<p>{{'jukebox.forbidden.hint' | i18n}}</p>
|
||||||
|
</mat-card-content>
|
||||||
|
<mat-card-actions>
|
||||||
|
</mat-card-actions>
|
||||||
|
</mat-card>
|
9
src/app/pages/jukebox/jukebox.component.scss
Normal file
9
src/app/pages/jukebox/jukebox.component.scss
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
mat-form-field {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
mat-card.track {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
136
src/app/pages/jukebox/jukebox.compontent.ts
Normal file
136
src/app/pages/jukebox/jukebox.compontent.ts
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
|
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||||
|
|
||||||
|
import { FormControl } from '@angular/forms';
|
||||||
|
|
||||||
|
import { ConfirmDialog } from '../../ui/confirm/confirm.component';
|
||||||
|
import { JukeboxService } from 'src/app/services/jukebox.service';
|
||||||
|
import { I18nService } from '../../services/i18n.service';
|
||||||
|
import { Data } from '@angular/router';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-jukebox',
|
||||||
|
templateUrl: './jukebox.component.html',
|
||||||
|
styleUrls: [ './jukebox.component.scss' ]
|
||||||
|
})
|
||||||
|
export class JukeboxComponent implements OnInit {
|
||||||
|
|
||||||
|
timeout: number = 0;
|
||||||
|
timer;
|
||||||
|
searchResult: any;
|
||||||
|
active: boolean = false;
|
||||||
|
wait: boolean = false;
|
||||||
|
forbidden: boolean = false;
|
||||||
|
unavailable: boolean = false;
|
||||||
|
searchDisabled = true;
|
||||||
|
searchFormControl = new FormControl();
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public dialog: MatDialog,
|
||||||
|
private snackBar: MatSnackBar,
|
||||||
|
private i18n: I18nService,
|
||||||
|
private jukeboxService: JukeboxService) { }
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.check();
|
||||||
|
|
||||||
|
this.searchFormControl.valueChanges.subscribe(value => {
|
||||||
|
this.searchDisabled = false;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
check() {
|
||||||
|
this.timeout = 0;
|
||||||
|
this.wait = false;
|
||||||
|
this.forbidden = false;
|
||||||
|
this.unavailable = false;
|
||||||
|
this.jukeboxService.check().subscribe((response) => {
|
||||||
|
this.active = true;
|
||||||
|
}, (error) => {
|
||||||
|
this.active = false;
|
||||||
|
if (error.status == 403) {
|
||||||
|
this.forbidden = true;
|
||||||
|
} else if (error.status == 410) {
|
||||||
|
this.unavailable = true;
|
||||||
|
} else if (error.status == 402) {
|
||||||
|
this.wait = true;
|
||||||
|
this.timeout = 60 - error.error;
|
||||||
|
this.timer = setInterval(() => {
|
||||||
|
this.timeout--;
|
||||||
|
if (this.timeout == 0) {
|
||||||
|
clearInterval(this.timer);
|
||||||
|
this.check();
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
search() {
|
||||||
|
this.searchDisabled = true;
|
||||||
|
this.jukeboxService.search(this.searchFormControl.value).subscribe((data: any) => {
|
||||||
|
this.searchResult = data.tracks;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
searchMore() {
|
||||||
|
this.searchDisabled = true;
|
||||||
|
this.jukeboxService.searchOffset(this.searchFormControl.value, this.searchResult.offset + this.searchResult.limit).subscribe((data: any) => {
|
||||||
|
this.searchResult.offset = data.tracks.offset;
|
||||||
|
this.searchResult.total = data.tracks.total;
|
||||||
|
for (let track of data.tracks.items) {
|
||||||
|
this.searchResult.items.push(track);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getImageUrl(track: any) {
|
||||||
|
let url = "";
|
||||||
|
let width;
|
||||||
|
if (track.album && track.album.images) {
|
||||||
|
for (let image of track.album.images) {
|
||||||
|
if (!width || width > image.width) {
|
||||||
|
url = image.url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
queue(track: any) {
|
||||||
|
const dialogRef = this.dialog.open(ConfirmDialog, {
|
||||||
|
data: {
|
||||||
|
'label': 'jukebox.addToQueue.confirm',
|
||||||
|
'args': [ track.artists[ 0 ].name, track.name ]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
dialogRef.afterClosed().subscribe(result => {
|
||||||
|
if (result) {
|
||||||
|
this.jukeboxService.queue(track.uri).subscribe(() => {
|
||||||
|
this.active = false;
|
||||||
|
this.wait = true;
|
||||||
|
this.timeout = 60;
|
||||||
|
this.timer = setInterval(() => {
|
||||||
|
this.timeout--;
|
||||||
|
if (this.timeout == 0) {
|
||||||
|
clearInterval(this.timer);
|
||||||
|
this.check();
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
|
this.snackBar.open(this.i18n.get("jukebox.addToQueue.success", []), this.i18n.get("close", []), {
|
||||||
|
duration: 3000
|
||||||
|
});
|
||||||
|
}, (error) => {
|
||||||
|
this.snackBar.open(this.i18n.get("jukebox.addToQueue.error", []), this.i18n.get("close", []), {
|
||||||
|
duration: 3000
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
src/app/services/jukebox.service.ts
Normal file
30
src/app/services/jukebox.service.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
|
||||||
|
import { environment } from '../../environments/environment';
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root',
|
||||||
|
})
|
||||||
|
export class JukeboxService {
|
||||||
|
|
||||||
|
constructor(private http: HttpClient) {
|
||||||
|
}
|
||||||
|
|
||||||
|
check() {
|
||||||
|
return this.http.get(environment.apiUrl + "/jukebox");
|
||||||
|
}
|
||||||
|
|
||||||
|
search(query: string) {
|
||||||
|
return this.http.get(environment.apiUrl + "/jukebox/search?q=" + query);
|
||||||
|
}
|
||||||
|
|
||||||
|
searchOffset(query: string, offset: number) {
|
||||||
|
return this.http.get(environment.apiUrl + "/jukebox/search?q=" + query + "&offset=" + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
queue(uri: string) {
|
||||||
|
return this.http.post(environment.apiUrl + "/jukebox/queue?uri=" + uri, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user