playlist download progress, fix library error downloading

This commit is contained in:
raiper34
2024-07-13 15:51:28 +02:00
parent 80a562c5cd
commit 06a8608f7c
15 changed files with 244 additions and 77 deletions
+6 -17
View File
@@ -14,7 +14,11 @@
<p class="subtitle">Download</p>
<div class="is-flex">
<input class="input" type="text" [(ngModel)]="url" placeholder="Paste playlist/song/artist url"/>
<button class="button is-primary" [class.is-loading]="(createLoading$ | async)?.isLoading" (click)="download()">
<button class="button is-primary"
[class.is-loading]="(createLoading$ | async)?.isLoading"
(click)="download()"
[disabled]="!url"
>
<i class="fa-solid fa-download"></i> Download
</button>
</div>
@@ -24,22 +28,7 @@
<div class="is-flex is-align-items-center">
<p class="subtitle">Playlists</p>&nbsp;
</div>
<article class="panel is-primary" *ngFor="let playlist of playlists$ | async">
<p class="panel-heading is-flex is-justify-content-space-between">
<span>
<i class="is-clickable fa-solid"
[ngClass]="playlist.collapsed ? 'fa-caret-up' : 'fa-caret-down'"
(click)="toggleCollapse(playlist.id)"
></i>&nbsp;
<span>{{playlist.name}}</span>
</span>
<a [href]="playlist.spotifyUrl" target="_blank" class="is-color-black"><i class="fa-brands fa-spotify"></i></a>
</p>
<ng-container *ngIf="playlist.collapsed">
<app-track-list [playlistId]="playlist.id"></app-track-list>
</ng-container>
</article>
<app-playlist-box *ngFor="let playlist of playlists$ | async" [playlist]="playlist"></app-playlist-box>
</div>
</div>
</section>
-18
View File
@@ -1,18 +0,0 @@
.panel-heading {
padding: 5px 10px;
border-radius: 10px;
}
.panel {
margin: 5px 0;
}
.loading-animation {
animation: spin 0.3s linear infinite;
}
@keyframes spin {
100% {
transform:rotate(360deg);
}
}
+6 -10
View File
@@ -1,23 +1,22 @@
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import {FormsModule} from "@angular/forms";
import {CommonModule, NgFor, NgSwitch, NgSwitchCase} from "@angular/common";
import {CommonModule, NgFor} from "@angular/common";
import {PlaylistService} from "./services/playlist.service";
import {TrackListComponent} from "./components/track-list/track-list.component";
import {PlaylistBoxComponent} from "./components/playlist-box/playlist-box.component";
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, RouterOutlet, FormsModule, NgFor, NgSwitch, NgSwitchCase, TrackListComponent],
imports: [CommonModule, RouterOutlet, FormsModule, NgFor, PlaylistBoxComponent],
templateUrl: './app.component.html',
styleUrl: './app.component.scss'
})
export class AppComponent {
url = ''
playlists$ = this.playlistService.all$;
loading$ = this.playlistService.loading$;
createLoading$ = this.playlistService.createLoading$;
playlists$ = this.playlistService.all$;
constructor(private readonly playlistService: PlaylistService) {
this.fetchPlaylists();
@@ -28,10 +27,7 @@ export class AppComponent {
}
download(): void {
this.playlistService.create(this.url);
}
toggleCollapse(playlistId: number): void {
this.playlistService.toggleCollapsed(playlistId);
this.url && this.playlistService.create(this.url);
this.url = '';
}
}
@@ -0,0 +1,21 @@
<article class="panel" [ngClass]="playlist.error ? 'is-danger' : 'is-primary'">
<p class="panel-heading is-flex is-justify-content-space-between">
<span>
<i class="is-clickable fa-solid"
[ngClass]="playlist.collapsed ? 'fa-caret-up' : 'fa-caret-down'"
(click)="toggleCollapse(playlist.id)"
></i>&nbsp;
<span *ngIf="playlist.error; else noErrorTemplate" class="is-size-6 has-text-weight-medium">{{playlist.error}}</span>
<ng-template #noErrorTemplate>
<span class="is-size-6 has-text-weight-semibold">{{playlist.name}}</span>
</ng-template>&nbsp;
<a [href]="playlist.spotifyUrl" target="_blank" class="is-color-black"><i class="fa-brands fa-spotify"></i></a>
</span>
<span *ngIf="!playlist.error">
<span class="is-size-6 has-text-weight-medium">{{trackCompletedCount$ | async}}/{{trackCount$ | async}}</span>&nbsp;
</span>
</p>
<ng-container *ngIf="playlist.collapsed">
<app-track-list [playlistId]="playlist.id"></app-track-list>
</ng-container>
</article>
@@ -0,0 +1,8 @@
.panel-heading {
padding: 5px 10px;
border-radius: 10px;
}
.panel {
margin: 5px 0;
}
@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PlaylistBoxComponent } from './playlist-box.component';
describe('PlaylistListComponent', () => {
let component: PlaylistBoxComponent;
let fixture: ComponentFixture<PlaylistBoxComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [PlaylistBoxComponent]
})
.compileComponents();
fixture = TestBed.createComponent(PlaylistBoxComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
@@ -0,0 +1,40 @@
import {Component, Input} from '@angular/core';
import {AsyncPipe, CommonModule, NgForOf, NgIf} from "@angular/common";
import {TrackListComponent} from "../track-list/track-list.component";
import {Playlist, PlaylistService, PlaylistUi} from "../../services/playlist.service";
import {Observable} from "rxjs";
@Component({
selector: 'app-playlist-box',
standalone: true,
imports: [
CommonModule,
AsyncPipe,
NgForOf,
NgIf,
TrackListComponent
],
templateUrl: './playlist-box.component.html',
styleUrl: './playlist-box.component.scss'
})
export class PlaylistBoxComponent {
@Input() set playlist(val: Playlist & PlaylistUi) {
this._playlist = val;
this.trackCount$ = this.playlistService.getTrackCount(val.id);
this.trackCompletedCount$ = this.playlistService.getCompletedTrackCount(val.id);
}
get playlist(): Playlist & PlaylistUi {
return this._playlist;
}
_playlist!: Playlist & PlaylistUi;
trackCount$!: Observable<number>;
trackCompletedCount$!: Observable<number>;
constructor(private readonly playlistService: PlaylistService) { }
toggleCollapse(playlistId: number): void {
this.playlistService.toggleCollapsed(playlistId);
}
}
+11 -2
View File
@@ -10,7 +10,7 @@ import {
withUIEntities
} from "@ngneat/elf-entities";
import {joinRequestResult, trackRequestResult} from "@ngneat/elf-requests";
import {map, tap} from "rxjs";
import {map, Observable, tap} from "rxjs";
import {Track, TrackService} from "./track.service";
import {Socket} from "ngx-socket-io";
@@ -20,8 +20,9 @@ const CREATE_LOADING = 'CREATE_LOADING';
export interface Playlist {
id: number;
name: string;
name?: string;
spotifyUrl: string;
error?: string;
createdAt: number;
}
@@ -61,6 +62,14 @@ export class PlaylistService {
);
}
getTrackCount(id: number): Observable<number> {
return this.trackService.getAllByPlaylist(id).pipe(map(data => data.length));
}
getCompletedTrackCount(id: number): Observable<number> {
return this.trackService.getCompletedByPlaylist(id).pipe(map(data => data.length));
}
fetch(): void {
this.http.get<Playlist[]>(ENDPOINT).pipe(
tap((data: Playlist[]) => this.store.update(
+6 -2
View File
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import {createStore} from "@ngneat/elf";
import {selectManyByPredicate, upsertEntities, withEntities} from "@ngneat/elf-entities";
import {Socket} from "ngx-socket-io";
import {tap} from "rxjs";
import {map, Observable, tap} from "rxjs";
import {trackRequestResult} from "@ngneat/elf-requests";
import {HttpClient} from "@angular/common/http";
@@ -39,10 +39,14 @@ export class TrackService {
withEntities<Track>(),
);
getAllByPlaylist(id: number) {
getAllByPlaylist(id: number): Observable<Track[]> {
return this.store.pipe(selectManyByPredicate(({playlistId}) => playlistId === id))
}
getCompletedByPlaylist(id: number): Observable<Track[]> {
return this.getAllByPlaylist(id).pipe(map(data => data.filter(item => item.status === TrackStatusEnum.Completed)));
}
constructor(
private readonly http: HttpClient,
private readonly socket: Socket,