refactor(playlist-backend): refactor playlist backend code

This commit is contained in:
raiper34
2024-09-30 13:55:08 +02:00
parent 0e88627ec7
commit 117110fae9
10 changed files with 222 additions and 176 deletions
+18 -11
View File
@@ -1,27 +1,34 @@
import {Body, Controller, Delete, Get, Param, Post, Put} from '@nestjs/common';
import {PlaylistService} from "./playlist.service";
import {PlaylistModel} from "./playlist.model";
import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Put,
} from '@nestjs/common';
import { PlaylistService } from './playlist.service';
import { PlaylistEntity } from './playlist.entity';
@Controller('playlist')
export class PlaylistController {
constructor(
private readonly service: PlaylistService,
) {
}
constructor(private readonly service: PlaylistService) {}
@Get()
getAll(): Promise<PlaylistModel[]> {
getAll(): Promise<PlaylistEntity[]> {
return this.service.findAll();
}
@Post()
async create(@Body() playlist: PlaylistModel): Promise<void> {
async create(@Body() playlist: PlaylistEntity): Promise<void> {
await this.service.create(playlist);
}
@Put(':id')
update(@Param('id') id: number, @Body() playlist: Partial<PlaylistModel>): Promise<void> {
update(
@Param('id') id: number,
@Body() playlist: Partial<PlaylistEntity>,
): Promise<void> {
return this.service.update(id, playlist);
}
+4 -5
View File
@@ -1,9 +1,8 @@
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
import {TrackEntity} from "../track/track.entity";
import { TrackEntity } from '../track/track.entity';
@Entity()
export class PlaylistEntity {
@PrimaryGeneratedColumn()
id: number;
@@ -16,12 +15,12 @@ export class PlaylistEntity {
@Column({ nullable: true })
error?: string;
@Column({default: false})
@Column({ default: false })
active?: boolean;
@Column({default: () => Date.now()})
@Column({ default: () => Date.now() })
createdAt?: number;
@OneToMany(() => TrackEntity, track => track.playlist)
@OneToMany(() => TrackEntity, (track) => track.playlist)
tracks?: TrackEntity[];
}
@@ -1,9 +0,0 @@
export interface PlaylistModel {
id: number;
name?: string;
spotifyUrl: string;
tracks?: any[]; //todo fix it
error?: string;
createdAt?: number;
active?: boolean;
}
+8 -6
View File
@@ -1,19 +1,21 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import {PlaylistEntity} from "./playlist.entity";
import {PlaylistService} from "./playlist.service";
import {PlaylistController} from "./playlist.controller";
import {TrackModule} from "../track/track.module";
import {ConfigModule} from "@nestjs/config";
import { PlaylistEntity } from './playlist.entity';
import { PlaylistService } from './playlist.service';
import { PlaylistController } from './playlist.controller';
import { TrackModule } from '../track/track.module';
import { ConfigModule } from '@nestjs/config';
import { SharedModule } from '../shared/shared.module';
@Module({
imports: [
TypeOrmModule.forFeature([PlaylistEntity]),
ConfigModule,
TrackModule,
SharedModule,
],
providers: [PlaylistService],
controllers: [PlaylistController],
exports: [PlaylistService]
exports: [PlaylistService],
})
export class PlaylistModule {}
+51 -37
View File
@@ -1,34 +1,40 @@
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 {resolve} from "path";
import {ConfigService} from "@nestjs/config";
import {EnviromentEnum} from "../enviroment.enum";
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 * as fs from 'fs';
import {Interval} from "@nestjs/schedule";
import {TrackStatusEnum} from "../track/track.entity";
import { Interval } from '@nestjs/schedule';
import { TrackStatusEnum } from '../track/track.entity';
import { UtilsService } from '../shared/utils.service';
const fetch = require('isomorphic-unfetch');
const { getData, getPreview, getTracks, getDetails } = require('spotify-url-info')(fetch);
const { getDetails } = require('spotify-url-info')(fetch);
enum WsPlaylistOperation {
New = 'playlistNew',
Update = 'playlistUpdate',
Delete = 'playlistDelete',
}
@WebSocketGateway()
@Injectable()
export class PlaylistService {
@WebSocketServer() io: Server;
constructor(
@InjectRepository(PlaylistEntity)
private repository: Repository<PlaylistEntity>,
private readonly trackService: TrackService,
private readonly configService: ConfigService,
private readonly utilsService: UtilsService,
) {}
findAll(relations: Record<string, boolean> = {tracks: true}, where?: Partial<PlaylistEntity>): Promise<PlaylistEntity[]> {
return this.repository.find({where, relations});
findAll(
relations: Record<string, boolean> = { tracks: true },
where?: Partial<PlaylistEntity>,
): Promise<PlaylistEntity[]> {
return this.repository.find({ where, relations });
}
findOne(id: number): Promise<PlaylistEntity | null> {
@@ -37,7 +43,7 @@ export class PlaylistService {
async remove(id: number): Promise<void> {
await this.repository.delete(id);
this.io.emit('playlistDelete', {id});
this.io.emit(WsPlaylistOperation.Delete, { id });
}
async create(playlist: PlaylistEntity): Promise<void> {
@@ -45,66 +51,74 @@ export class PlaylistService {
let playlist2Save: PlaylistEntity;
try {
details = await getDetails(playlist.spotifyUrl);
playlist2Save = {...playlist, name: details.preview.title};
playlist2Save = { ...playlist, name: details.preview.title };
} catch (err) {
playlist2Save = {...playlist, error: String(err)};
playlist2Save = { ...playlist, error: String(err) };
}
const savedPlaylist = await this.save(playlist2Save);
this.createPlaylistFolderStructure(savedPlaylist.name);
for(let track of details?.tracks ?? []) {
await this.trackService.create({
for (const track of details?.tracks ?? []) {
await this.trackService.create(
{
artist: track.artist,
name: track.name,
spotifyUrl: track.previewUrl,
}, savedPlaylist);
},
savedPlaylist,
);
}
}
async save(playlist: PlaylistEntity): Promise<PlaylistEntity> {
const savedPlaylist = await this.repository.save(playlist);
this.io.emit('playlistNew', savedPlaylist);
this.io.emit(WsPlaylistOperation.New, savedPlaylist);
return savedPlaylist;
}
async update(id: number, playlist: Partial<PlaylistEntity>): Promise<void> {
await this.repository.update(id, playlist);
const dbPlaylist = await this.findOne(id);
this.io.emit('playlistUpdate', dbPlaylist);
this.io.emit(WsPlaylistOperation.Update, dbPlaylist);
}
async retryFailedOfPlaylist(id: number): Promise<void> {
const tracks = await this.trackService.getAllByPlaylist(id);
for(let track of tracks) {
for (const track of tracks) {
if (track.status === TrackStatusEnum.Error) {
await this.trackService.retry(track.id);
}
}
}
private createPlaylistFolderStructure(playlistName): void {
const folderName = resolve(__dirname, '..',this.configService.get<string>(EnviromentEnum.DOWNLOADS_PATH), playlistName);
!fs.existsSync(folderName) && fs.mkdirSync(folderName);
private createPlaylistFolderStructure(playlistName: string): void {
const playlistPath = this.utilsService.getPlaylistFolderPath(playlistName);
!fs.existsSync(playlistPath) && fs.mkdirSync(playlistPath);
}
@Interval(3600000)
@Interval(3_600_000)
async checkActivePlaylists(): Promise<void> {
const activePlaylists = await this.findAll({}, {active: true});
for (let playlist of activePlaylists) {
const activePlaylists = await this.findAll({}, { active: true });
for (const playlist of activePlaylists) {
let details;
try {
details = await getDetails(playlist.spotifyUrl);
} catch (err) {
await this.update(playlist.id, {...playlist, error: String(err)});
await this.update(playlist.id, { ...playlist, error: String(err) });
}
this.createPlaylistFolderStructure(playlist.name);
for(let track of details?.tracks ?? []) {
for (const track of details?.tracks ?? []) {
const track2Save = {
artist: track.artist,
name: track.name,
spotifyUrl: track.previewUrl,
}
const isExist = !!(await this.trackService.findAll({...track2Save, playlist: {id: playlist.id}})).length;
if(!isExist) {
};
const isExist = !!(
await this.trackService.findAll({
...track2Save,
playlist: { id: playlist.id },
})
).length;
if (!isExist) {
await this.trackService.create(track2Save, playlist);
}
}
+11
View File
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { UtilsService } from './utils.service';
import { ConfigModule } from '@nestjs/config';
@Module({
imports: [ConfigModule],
providers: [UtilsService],
controllers: [],
exports: [UtilsService],
})
export class SharedModule {}
+18
View File
@@ -0,0 +1,18 @@
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { resolve } from 'path';
import { EnviromentEnum } from '../enviroment.enum';
@Injectable()
export class UtilsService {
constructor(private readonly configService: ConfigService) {}
getPlaylistFolderPath(name: string): string {
return resolve(
__dirname,
'..',
this.configService.get<string>(EnviromentEnum.DOWNLOADS_PATH),
name,
);
}
}
+6 -1
View File
@@ -4,9 +4,14 @@ import { TrackEntity } from './track.entity';
import { TrackService } from './track.service';
import { TrackController } from './track.controller';
import { ConfigModule } from '@nestjs/config';
import { SharedModule } from '../shared/shared.module';
@Module({
imports: [TypeOrmModule.forFeature([TrackEntity]), ConfigModule],
imports: [
TypeOrmModule.forFeature([TrackEntity]),
ConfigModule,
SharedModule,
],
providers: [TrackService],
controllers: [TrackController],
exports: [TrackService],
+3 -4
View File
@@ -13,6 +13,7 @@ import { WebSocketGateway, WebSocketServer } from '@nestjs/websockets';
import { Server } from 'socket.io';
import * as ffmpeg from 'fluent-ffmpeg';
import { EnviromentEnum } from '../enviroment.enum';
import {UtilsService} from "../shared/utils.service";
enum WsTrackOperation {
New = 'trackNew',
@@ -30,6 +31,7 @@ export class TrackService {
@InjectRepository(TrackEntity)
private repository: Repository<TrackEntity>,
private readonly configService: ConfigService,
private readonly utilsService: UtilsService,
) {}
findAll(
@@ -153,10 +155,7 @@ export class TrackService {
getFolderName(track: TrackEntity, playlist: PlaylistEntity): string {
return resolve(
__dirname,
'..',
this.configService.get<string>(EnviromentEnum.DOWNLOADS_PATH),
playlist.name,
this.utilsService.getPlaylistFolderPath(playlist.name),
this.getTrackFileName(track),
);
}