Release Jarri Spooty Workspace v3.0.0

This commit is contained in:
TorMatzAndren
2026-05-23 01:01:55 +02:00
parent 18deaa2d90
commit 56534d1d33
21 changed files with 1493 additions and 163 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "backend",
"version": "2.4.2",
"version": "3.0.0",
"description": "",
"author": "",
"private": true,
+2
View File
@@ -14,6 +14,7 @@ import { BullModule } from '@nestjs/bullmq';
import { APP_GUARD } from '@nestjs/core';
import { AuthGuard } from './shared/auth.guard';
import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
import { ArchiveModule } from './archive/archive.module';
@Module({
imports: [
@@ -79,6 +80,7 @@ import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
}),
TrackModule,
PlaylistModule,
ArchiveModule,
],
controllers: [AppController],
providers: [
@@ -0,0 +1,12 @@
import { Controller, Get } from '@nestjs/common';
import { ArchiveListingDto, ArchiveService } from './archive.service';
@Controller('archive')
export class ArchiveController {
constructor(private readonly archiveService: ArchiveService) {}
@Get()
list(): Promise<ArchiveListingDto> {
return this.archiveService.list();
}
}
+11
View File
@@ -0,0 +1,11 @@
import { Module } from '@nestjs/common';
import { SharedModule } from '../shared/shared.module';
import { ArchiveController } from './archive.controller';
import { ArchiveService } from './archive.service';
@Module({
imports: [SharedModule],
controllers: [ArchiveController],
providers: [ArchiveService],
})
export class ArchiveModule {}
@@ -0,0 +1,62 @@
import { Injectable } from '@nestjs/common';
import { promises as fs } from 'fs';
import { relative, resolve } from 'path';
import { UtilsService } from '../shared/utils.service';
export interface ArchiveFileDto {
name: string;
path: string;
sizeBytes: number;
modifiedAt: number;
}
export interface ArchiveListingDto {
root: string;
files: ArchiveFileDto[];
}
@Injectable()
export class ArchiveService {
constructor(private readonly utilsService: UtilsService) {}
async list(): Promise<ArchiveListingDto> {
const root = this.utilsService.getRootDownloadsPath();
const files = await this.listFiles(root, root);
return {
root,
files: files.sort((a, b) => b.modifiedAt - a.modifiedAt),
};
}
private async listFiles(root: string, currentPath: string): Promise<ArchiveFileDto[]> {
const entries = await fs.readdir(currentPath, { withFileTypes: true });
const files: ArchiveFileDto[] = [];
for (const entry of entries) {
const entryPath = this.utilsService.ensureInsideDownloadsRoot(
resolve(currentPath, entry.name),
);
if (entry.isDirectory()) {
files.push(...await this.listFiles(root, entryPath));
continue;
}
if (!entry.isFile()) {
continue;
}
const stat = await fs.stat(entryPath);
files.push({
name: entry.name,
path: relative(root, entryPath).split(/[\\/]+/).join('/'),
sizeBytes: stat.size,
modifiedAt: stat.mtimeMs,
});
}
return files;
}
}
+6 -1
View File
@@ -20,6 +20,10 @@ enum WsTrackOperation {
Delete = 'trackDelete',
}
type ClientTrack = Omit<Partial<TrackEntity>, 'rejectedYoutubeUrls'> & {
rejectedYoutubeUrls?: string[];
};
@WebSocketGateway()
@Injectable()
export class TrackService {
@@ -56,13 +60,14 @@ export class TrackService {
this.io.emit(WsTrackOperation.Delete, { id });
}
private toClientTrack(track: TrackEntity): Partial<TrackEntity> {
private toClientTrack(track: TrackEntity): ClientTrack {
return {
id: track.id,
artist: track.artist,
name: track.name,
spotifyUrl: track.spotifyUrl,
youtubeUrl: track.youtubeUrl,
rejectedYoutubeUrls: this.parseRejectedYoutubeUrls(track),
downloadAttemptCount: track.downloadAttemptCount,
status: track.status,
error: track.error,