diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index b59905c..6aee5eb 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -32,10 +32,13 @@ import { BorrowItemEditComponent, BorrowItemsComponent } from './pages/borrow/it
import { BorrowRequestsComponent } from './pages/borrow/requests/requests.component';
import { BorrowComponent } from './pages/borrow/borrow.component';
import { InviteCodeComponent } from './pages/invites/code/code.component';
+import { JukeboxComponent } from './pages/jukebox/jukebox.compontent';
const routes: Routes = [
{ path: 'profile/:username', component: UserComponent, canActivate: [ AuthUpdateGuard ] },
{ 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: '', redirectTo: "/services", pathMatch: 'full' },
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 37c234b..edba6ac 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -65,6 +65,7 @@ import { MatPaginatorIntl } from '@angular/material/paginator';
import { NgxMatDateAdapter, NGX_MAT_DATE_FORMATS } from '@angular-material-components/datetime-picker';
import { MAT_DATE_LOCALE } from '@angular/material/core';
import { NgxMatMomentAdapter } from '@angular-material-components/moment-adapter';
+import { JukeboxComponent } from './pages/jukebox/jukebox.compontent';
export function init_app(i18n: I18nService) {
@@ -124,7 +125,8 @@ export class XhrInterceptor implements HttpInterceptor {
UrlShortenerComponent, UrlShortenerShareDialog, UrlShortenerEditDialog, UrlShortenerPasswordComponent,
BorrowComponent, BorrowItemsComponent, BorrowItemEditComponent, BorrowRequestsComponent, BorrowRequestEditComponent, BorrowProvingComponent, BorrowProvingResultDialog,
DividerComponent, DividertestComponent,
- DurationpickerComponent
+ DurationpickerComponent,
+ JukeboxComponent
],
imports: [
BrowserModule,
diff --git a/src/app/pages/jukebox/jukebox.component.html b/src/app/pages/jukebox/jukebox.component.html
new file mode 100644
index 0000000..a5f5731
--- /dev/null
+++ b/src/app/pages/jukebox/jukebox.component.html
@@ -0,0 +1,75 @@
+
+
+
+ {{'jukebox' | i18n}}
+
+
+
+
+
+
+
+
+
+
{{'jukebox.timeout' | i18n:timeout}}
+
+
+
+
+
+ {{track.name}}
+
+
+ {{artist.name}},
+
+
+
+
+
+
+
+
+
+
+
+ {{'jukebox' | i18n}}
+ {{'jukebox.wait' | i18n}}
+
+
+ {{'jukebox.timeout' | i18n:timeout}}
+
+
+
+
+
+
+
+ {{'jukebox' | i18n}}
+ {{'jukebox.unavailable' | i18n}}
+
+
+ {{'jukebox.unavailable.hint' | i18n}}
+
+
+
+
+
+
+
+ {{'jukebox' | i18n}}
+ {{'jukebox.forbidden' | i18n}}
+
+
+ {{'jukebox.forbidden.hint' | i18n}}
+
+
+
+
\ No newline at end of file
diff --git a/src/app/pages/jukebox/jukebox.component.scss b/src/app/pages/jukebox/jukebox.component.scss
new file mode 100644
index 0000000..781e60f
--- /dev/null
+++ b/src/app/pages/jukebox/jukebox.component.scss
@@ -0,0 +1,9 @@
+mat-form-field {
+ display: block;
+}
+
+mat-card.track {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ cursor: pointer;
+}
\ No newline at end of file
diff --git a/src/app/pages/jukebox/jukebox.compontent.ts b/src/app/pages/jukebox/jukebox.compontent.ts
new file mode 100644
index 0000000..6994745
--- /dev/null
+++ b/src/app/pages/jukebox/jukebox.compontent.ts
@@ -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
+ });
+ })
+ }
+ });
+ }
+
+}
\ No newline at end of file
diff --git a/src/app/services/jukebox.service.ts b/src/app/services/jukebox.service.ts
new file mode 100644
index 0000000..dc77b7c
--- /dev/null
+++ b/src/app/services/jukebox.service.ts
@@ -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, "");
+ }
+
+}
\ No newline at end of file