From da78603db2a561aba454a66372abb6677cc76bc4 Mon Sep 17 00:00:00 2001 From: raiper34 Date: Sat, 20 Jul 2024 14:38:09 +0200 Subject: [PATCH] clear playlist mass operations and retry functionality --- .../src/playlist/playlist.controller.ts | 5 +++ src/backend/src/playlist/playlist.service.ts | 17 ++++++-- src/backend/src/track/track.controller.ts | 5 +++ src/backend/src/track/track.service.ts | 8 +++- src/frontend/src/app/app.component.html | 12 +++++- src/frontend/src/app/app.component.ts | 14 +++++-- .../playlist-box/playlist-box.component.html | 3 +- .../playlist-box/playlist-box.component.ts | 16 +++++--- .../track-list/track-list.component.html | 1 + .../track-list/track-list.component.ts | 4 ++ .../src/app/services/playlist.service.ts | 41 +++++++++++++++---- .../src/app/services/track.service.ts | 4 ++ 12 files changed, 106 insertions(+), 24 deletions(-) diff --git a/src/backend/src/playlist/playlist.controller.ts b/src/backend/src/playlist/playlist.controller.ts index 15e6f3f..0ccb2ff 100644 --- a/src/backend/src/playlist/playlist.controller.ts +++ b/src/backend/src/playlist/playlist.controller.ts @@ -24,4 +24,9 @@ export class PlaylistController { remove(@Param('id') id: number): Promise { return this.service.remove(id); } + + @Get('retry/:id') + retryFailedOfPlaylist(@Param('id') id: number): Promise { + return this.service.retryFailedOfPlaylist(id); + } } \ No newline at end of file diff --git a/src/backend/src/playlist/playlist.service.ts b/src/backend/src/playlist/playlist.service.ts index d6d1be7..010ae35 100644 --- a/src/backend/src/playlist/playlist.service.ts +++ b/src/backend/src/playlist/playlist.service.ts @@ -1,10 +1,12 @@ -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; +import {Injectable} from '@nestjs/common'; +import {InjectRepository} from '@nestjs/typeorm'; +import {Repository} from 'typeorm'; import {PlaylistEntity} from "./playlist.entity"; import {TrackService} from "../track/track.service"; import {WebSocketGateway, WebSocketServer} from "@nestjs/websockets"; import {Server} from "socket.io"; +import {TrackStatusEnum} from "../track/track.model"; + const fetch = require('isomorphic-unfetch'); const { getData, getPreview, getTracks, getDetails } = require('spotify-url-info')(fetch); @@ -57,4 +59,13 @@ export class PlaylistService { await this.repository.update(id, playlist); this.io.emit('trackPlaylist', playlist); } + + async retryFailedOfPlaylist(id: number): Promise { + const tracks = await this.trackService.getAllByPlaylist(id); + for(let track of tracks) { + if (track.status === TrackStatusEnum.Error) { + await this.trackService.retry(track.id); + } + } + } } \ No newline at end of file diff --git a/src/backend/src/track/track.controller.ts b/src/backend/src/track/track.controller.ts index 1d00858..9ee9d82 100644 --- a/src/backend/src/track/track.controller.ts +++ b/src/backend/src/track/track.controller.ts @@ -35,4 +35,9 @@ export class TrackController { remove(@Param('id') id: number): Promise { return this.service.remove(id); } + + @Get('retry/:id') + retry(@Param('id') id: number): Promise { + return this.service.retry(id); + } } \ No newline at end of file diff --git a/src/backend/src/track/track.service.ts b/src/backend/src/track/track.service.ts index e946f1e..a6e6091 100644 --- a/src/backend/src/track/track.service.ts +++ b/src/backend/src/track/track.service.ts @@ -6,13 +6,13 @@ import {PlaylistEntity} from "../playlist/playlist.entity"; import {Interval} from "@nestjs/schedule"; import {TrackStatusEnum} from "./track.model"; import * as yts from 'yt-search'; +import {SearchResult} from 'yt-search'; import * as ytdl from '@distube/ytdl-core'; import * as fs from 'fs'; import {ConfigService} from "@nestjs/config"; import {resolve} from "path"; import {WebSocketGateway, WebSocketServer} from "@nestjs/websockets"; import {Server} from "socket.io"; -import {SearchResult} from "yt-search"; @WebSocketGateway() @Injectable() @@ -53,6 +53,11 @@ export class TrackService { this.io.emit('trackUpdate', track); } + async retry(id: number): Promise { + const track = await this.findOne(id); + await this.update(id, {...track, status: TrackStatusEnum.New}); + } + @Interval(1000) async findOnYoutube() { const newTracks = await this.findAll({status: TrackStatusEnum.New}); @@ -66,6 +71,7 @@ export class TrackService { youtubeResult = await yts(`${track.artist} - ${track.song}`); updatedTrack = {...track, youtubeUrl: youtubeResult.videos[0].url, status: TrackStatusEnum.Queued}; } catch (err) { + console.log(err); updatedTrack = {...track, error: String(err), status: TrackStatusEnum.Error}; } await this.update(track.id, updatedTrack); diff --git a/src/frontend/src/app/app.component.html b/src/frontend/src/app/app.component.html index ee06d43..79e4e28 100644 --- a/src/frontend/src/app/app.component.html +++ b/src/frontend/src/app/app.component.html @@ -25,8 +25,16 @@
-
-

Playlists

  +
+

Playlists

+
+ + +
diff --git a/src/frontend/src/app/app.component.ts b/src/frontend/src/app/app.component.ts index 492e076..4b26a71 100644 --- a/src/frontend/src/app/app.component.ts +++ b/src/frontend/src/app/app.component.ts @@ -1,8 +1,8 @@ -import { Component } from '@angular/core'; -import { RouterOutlet } from '@angular/router'; +import {Component} from '@angular/core'; +import {RouterOutlet} from '@angular/router'; import {FormsModule} from "@angular/forms"; import {CommonModule, NgFor} from "@angular/common"; -import {PlaylistService} from "./services/playlist.service"; +import {PlaylistService, PlaylistStatusEnum} from "./services/playlist.service"; import {PlaylistBoxComponent} from "./components/playlist-box/playlist-box.component"; @Component({ @@ -30,4 +30,12 @@ export class AppComponent { this.url && this.playlistService.create(this.url); this.url = ''; } + + deleteCompleted(): void { + this.playlistService.deleteAllByStatus(PlaylistStatusEnum.Completed); + } + + deleteFailed(): void { + this.playlistService.deleteAllByStatus(PlaylistStatusEnum.Error); + } } diff --git a/src/frontend/src/app/components/playlist-box/playlist-box.component.html b/src/frontend/src/app/components/playlist-box/playlist-box.component.html index 63a5a86..499aab3 100644 --- a/src/frontend/src/app/components/playlist-box/playlist-box.component.html +++ b/src/frontend/src/app/components/playlist-box/playlist-box.component.html @@ -16,7 +16,8 @@ -   +   +   {{trackCompletedCount$ | async}}/{{trackCount$ | async}} 

diff --git a/src/frontend/src/app/components/playlist-box/playlist-box.component.ts b/src/frontend/src/app/components/playlist-box/playlist-box.component.ts index 36c6b01..d930575 100644 --- a/src/frontend/src/app/components/playlist-box/playlist-box.component.ts +++ b/src/frontend/src/app/components/playlist-box/playlist-box.component.ts @@ -1,15 +1,15 @@ 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, PLaylistStatusEnum, PlaylistUi} from "../../services/playlist.service"; +import {Playlist, PlaylistService, PlaylistStatusEnum, PlaylistUi} from "../../services/playlist.service"; import {Observable, map} from "rxjs"; const STATUS2CLASS = { - [PLaylistStatusEnum.Completed]: 'is-success', - [PLaylistStatusEnum.InProgress]: 'is-info', - [PLaylistStatusEnum.Warning]: 'is-warning', - [PLaylistStatusEnum.Error]: 'is-danger', - [PLaylistStatusEnum.Subscribed]: 'is-primary', + [PlaylistStatusEnum.Completed]: 'is-success', + [PlaylistStatusEnum.InProgress]: 'is-info', + [PlaylistStatusEnum.Warning]: 'is-warning', + [PlaylistStatusEnum.Error]: 'is-danger', + [PlaylistStatusEnum.Subscribed]: 'is-primary', } @Component({ @@ -53,4 +53,8 @@ export class PlaylistBoxComponent { delete(id: number): void { this.service.delete(id); } + + retryFailed(id: number): void { + this.service.retryFailed(id); + } } diff --git a/src/frontend/src/app/components/track-list/track-list.component.html b/src/frontend/src/app/components/track-list/track-list.component.html index 57151ac..3160a5c 100644 --- a/src/frontend/src/app/components/track-list/track-list.component.html +++ b/src/frontend/src/app/components/track-list/track-list.component.html @@ -20,6 +20,7 @@
+     New diff --git a/src/frontend/src/app/components/track-list/track-list.component.ts b/src/frontend/src/app/components/track-list/track-list.component.ts index 3b87eaf..fa7ded2 100644 --- a/src/frontend/src/app/components/track-list/track-list.component.ts +++ b/src/frontend/src/app/components/track-list/track-list.component.ts @@ -25,4 +25,8 @@ export class TrackListComponent { delete(id: number): void { this.service.delete(id); } + + retry(id: number): void { + this.service.retry(id); + } } diff --git a/src/frontend/src/app/services/playlist.service.ts b/src/frontend/src/app/services/playlist.service.ts index 256ac0e..d477412 100644 --- a/src/frontend/src/app/services/playlist.service.ts +++ b/src/frontend/src/app/services/playlist.service.ts @@ -13,7 +13,7 @@ import { withUIEntities } from "@ngneat/elf-entities"; import {joinRequestResult, trackRequestResult} from "@ngneat/elf-requests"; -import {combineLatest, map, Observable, tap} from "rxjs"; +import {combineLatest, filter, first, map, Observable, of, switchMap, tap} from "rxjs"; import {Track, TrackService} from "./track.service"; import {Socket} from "ngx-socket-io"; @@ -34,7 +34,7 @@ export interface PlaylistUi { collapsed: boolean; } -export enum PLaylistStatusEnum { +export enum PlaylistStatusEnum { InProgress, Completed, Warning, @@ -90,7 +90,7 @@ export class PlaylistService { return this.trackService.getErrorByPlaylist(id).pipe(map(data => data.length)); } - getStatus$(id: number): Observable { + getStatus$(id: number): Observable { return combineLatest([ this.getById(id), this.getTrackCount(id), @@ -98,16 +98,33 @@ export class PlaylistService { this.getErrorTrackCount(id), ]).pipe(map(([playlist, trackCount, completedCount, errorCount]) => { if (playlist?.error || errorCount === trackCount) { - return PLaylistStatusEnum.Error; + return PlaylistStatusEnum.Error; } else if (trackCount === completedCount) { - return PLaylistStatusEnum.Completed; + return PlaylistStatusEnum.Completed; } else if (errorCount > 1) { - return PLaylistStatusEnum.Warning; + return PlaylistStatusEnum.Warning; } - return PLaylistStatusEnum.InProgress; + return PlaylistStatusEnum.InProgress; })); } + deleteAllByStatus(status: PlaylistStatusEnum): void { + this.all$.pipe( + first(), + switchMap(playlists => + combineLatest(playlists.map(item => this.deleteIfStatusEquals$(item.id, status))) + ) + ).subscribe(); + } + + private deleteIfStatusEquals$(id: number, status2Filter: PlaylistStatusEnum): Observable { + return combineLatest([of(id), this.getStatus$(id)]).pipe( + first(), + filter(([_, status]) => status === status2Filter), + switchMap(([id]) => this.delete$(id)), + ); + } + fetch(): void { this.http.get(ENDPOINT).pipe( tap((data: Playlist[]) => this.store.update( @@ -130,6 +147,14 @@ export class PlaylistService { } delete(id: number): void { - this.http.delete(`${ENDPOINT}/${id}`).subscribe(); + this.delete$(id).subscribe(); + } + + private delete$(id: number): Observable { + return this.http.delete(`${ENDPOINT}/${id}`); + } + + retryFailed(id: number): void { + this.http.get(`${ENDPOINT}/retry/${id}`).subscribe(); } } diff --git a/src/frontend/src/app/services/track.service.ts b/src/frontend/src/app/services/track.service.ts index e05eed5..b8be717 100644 --- a/src/frontend/src/app/services/track.service.ts +++ b/src/frontend/src/app/services/track.service.ts @@ -75,4 +75,8 @@ export class TrackService { delete(id: number): void { this.http.delete(`${ENDPOINT}/${id}`).subscribe(); } + + retry(id: number): void { + this.http.get(`${ENDPOINT}/retry/${id}`).subscribe(); + } }