11import { UPLOAD_CONSTANTS } from '@nbw/config' ;
2- import type { UserDocument } from '@nbw/database' ;
32import {
3+ PageDto ,
4+ UserDocument ,
45 PageQueryDTO ,
56 SongPreviewDto ,
67 SongViewDto ,
78 UploadSongDto ,
89 UploadSongResponseDto ,
10+ FeaturedSongsDto ,
911} from '@nbw/database' ;
1012import type { RawBodyRequest } from '@nestjs/common' ;
1113import {
14+ BadRequestException ,
1215 Body ,
1316 Controller ,
1417 Delete ,
@@ -34,6 +37,9 @@ import {
3437 ApiBody ,
3538 ApiConsumes ,
3639 ApiOperation ,
40+ ApiParam ,
41+ ApiQuery ,
42+ ApiResponse ,
3743 ApiTags ,
3844} from '@nestjs/swagger' ;
3945import type { Response } from 'express' ;
@@ -43,20 +49,14 @@ import { GetRequestToken, validateUser } from '@server/lib/GetRequestUser';
4349
4450import { SongService } from './song.service' ;
4551
46- // Handles public-facing song routes.
47-
4852@Controller ( 'song' )
4953@ApiTags ( 'song' )
5054export class SongController {
5155 static multerConfig : MulterOptions = {
52- limits : {
53- fileSize : UPLOAD_CONSTANTS . file . maxSize ,
54- } ,
56+ limits : { fileSize : UPLOAD_CONSTANTS . file . maxSize } ,
5557 fileFilter : ( req , file , cb ) => {
56- if ( ! file . originalname . match ( / \. ( n b s ) $ / ) ) {
58+ if ( ! file . originalname . match ( / \. ( n b s ) $ / ) )
5759 return cb ( new Error ( 'Only .nbs files are allowed!' ) , false ) ;
58- }
59-
6060 cb ( null , true ) ;
6161 } ,
6262 } ;
@@ -68,12 +68,170 @@ export class SongController {
6868
6969 @Get ( '/' )
7070 @ApiOperation ( {
71- summary : 'Get a filtered/sorted list of songs with pagination' ,
71+ summary : 'Get songs with various filtering and browsing options' ,
72+ description : `
73+ Retrieves songs based on the provided query parameters. Supports multiple modes:
74+
75+ **Default mode** (no 'q' parameter): Returns paginated songs with sorting/filtering
76+
77+ **Special query modes** (using 'q' parameter):
78+ - \`featured\`: Get recent popular songs with pagination
79+ - \`recent\`: Get recently uploaded songs with pagination
80+ - \`categories\`:
81+ - Without 'id': Returns a record of available categories and their song counts
82+ - With 'id': Returns songs from the specified category with pagination
83+ - \`random\`: Returns random songs (requires 'count' parameter, 1-10 songs, optionally filtered by 'category')
84+
85+ **Query Parameters:**
86+ - Standard pagination/sorting via PageQueryDTO (page, limit, sort, order, timespan)
87+ - \`q\`: Special query mode ('featured', 'recent', 'categories', 'random')
88+ - \`id\`: Category ID (used with q=categories to get songs from specific category)
89+ - \`count\`: Number of random songs to return (1-10, used with q=random)
90+ - \`category\`: Category filter for random songs (used with q=random)
91+
92+ **Return Types:**
93+ - SongPreviewDto[]: Array of song previews (most cases)
94+ - Record<string, number>: Category name to count mapping (when q=categories without id)
95+ ` ,
96+ } )
97+ @ApiQuery ( {
98+ name : 'q' ,
99+ required : false ,
100+ enum : [ 'featured' , 'recent' , 'categories' , 'random' ] ,
101+ description :
102+ 'Special query mode. If not provided, returns standard paginated song list.' ,
103+ example : 'recent' ,
104+ } )
105+ @ApiParam ( {
106+ name : 'id' ,
107+ required : false ,
108+ type : 'string' ,
109+ description :
110+ 'Category ID. Only used when q=categories to get songs from a specific category.' ,
111+ example : 'pop' ,
112+ } )
113+ @ApiQuery ( {
114+ name : 'count' ,
115+ required : false ,
116+ type : 'string' ,
117+ description :
118+ 'Number of random songs to return (1-10). Only used when q=random.' ,
119+ example : '5' ,
120+ } )
121+ @ApiQuery ( {
122+ name : 'category' ,
123+ required : false ,
124+ type : 'string' ,
125+ description : 'Category filter for random songs. Only used when q=random.' ,
126+ example : 'electronic' ,
127+ } )
128+ @ApiResponse ( {
129+ status : 200 ,
130+ description :
131+ 'Success. Returns either an array of song previews or category counts.' ,
132+ schema : {
133+ oneOf : [
134+ {
135+ type : 'array' ,
136+ items : { $ref : '#/components/schemas/SongPreviewDto' } ,
137+ description :
138+ 'Array of song previews (default behavior and most query modes)' ,
139+ } ,
140+ {
141+ type : 'object' ,
142+ additionalProperties : { type : 'number' } ,
143+ description :
144+ 'Category name to song count mapping (only when q=categories without id)' ,
145+ example : { pop : 42 , rock : 38 , electronic : 15 } ,
146+ } ,
147+ ] ,
148+ } ,
149+ } )
150+ @ApiResponse ( {
151+ status : 400 ,
152+ description :
153+ 'Bad Request. Invalid query parameters (e.g., invalid count for random query).' ,
72154 } )
73155 public async getSongList (
74156 @Query ( ) query : PageQueryDTO ,
75- ) : Promise < SongPreviewDto [ ] > {
76- return await this . songService . getSongByPage ( query ) ;
157+ @Query ( 'q' ) q ?: 'featured' | 'recent' | 'categories' | 'random' ,
158+ @Param ( 'id' ) id ?: string ,
159+ @Query ( 'category' ) category ?: string ,
160+ ) : Promise <
161+ PageDto < SongPreviewDto > | Record < string , number > | FeaturedSongsDto
162+ > {
163+ if ( q ) {
164+ switch ( q ) {
165+ case 'featured' :
166+ return await this . songService . getFeaturedSongs ( ) ;
167+ case 'recent' :
168+ return new PageDto < SongPreviewDto > ( {
169+ content : await this . songService . getRecentSongs (
170+ query . page ,
171+ query . limit ,
172+ ) ,
173+ page : query . page ,
174+ limit : query . limit ,
175+ total : 0 ,
176+ } ) ;
177+ case 'categories' :
178+ if ( id ) {
179+ return new PageDto < SongPreviewDto > ( {
180+ content : await this . songService . getSongsByCategory (
181+ category ,
182+ query . page ,
183+ query . limit ,
184+ ) ,
185+ page : query . page ,
186+ limit : query . limit ,
187+ total : 0 ,
188+ } ) ;
189+ }
190+ return await this . songService . getCategories ( ) ;
191+ case 'random' : {
192+ if ( query . limit && ( query . limit < 1 || query . limit > 10 ) ) {
193+ throw new BadRequestException ( 'Invalid query parameters' ) ;
194+ }
195+ const data = await this . songService . getRandomSongs (
196+ query . limit ?? 1 ,
197+ category ,
198+ ) ;
199+ return new PageDto < SongPreviewDto > ( {
200+ content : data ,
201+ page : query . page ,
202+ limit : query . limit ,
203+ total : data . length ,
204+ } ) ;
205+ }
206+ default :
207+ throw new BadRequestException ( 'Invalid query parameters' ) ;
208+ }
209+ }
210+
211+ const data = await this . songService . getSongByPage ( query ) ;
212+ return new PageDto < SongPreviewDto > ( {
213+ content : data ,
214+ page : query . page ,
215+ limit : query . limit ,
216+ total : data . length ,
217+ } ) ;
218+ }
219+
220+ @Get ( '/search' )
221+ @ApiOperation ( {
222+ summary : 'Search songs by keywords with pagination and sorting' ,
223+ } )
224+ public async searchSongs (
225+ @Query ( ) query : PageQueryDTO ,
226+ @Query ( 'q' ) q : string ,
227+ ) : Promise < PageDto < SongPreviewDto > > {
228+ const data = await this . songService . searchSongs ( query , q ?? '' ) ;
229+ return new PageDto < SongPreviewDto > ( {
230+ content : data ,
231+ page : query . page ,
232+ limit : query . limit ,
233+ total : data . length ,
234+ } ) ;
77235 }
78236
79237 @Get ( '/:id' )
@@ -101,10 +259,7 @@ export class SongController {
101259 @UseGuards ( AuthGuard ( 'jwt-refresh' ) )
102260 @ApiBearerAuth ( )
103261 @ApiOperation ( { summary : 'Edit song info by ID' } )
104- @ApiBody ( {
105- description : 'Upload Song' ,
106- type : UploadSongResponseDto ,
107- } )
262+ @ApiBody ( { description : 'Upload Song' , type : UploadSongResponseDto } )
108263 public async patchSong (
109264 @Param ( 'id' ) id : string ,
110265 @Req ( ) req : RawBodyRequest < Request > ,
@@ -174,10 +329,7 @@ export class SongController {
174329 @UseGuards ( AuthGuard ( 'jwt-refresh' ) )
175330 @ApiBearerAuth ( )
176331 @ApiConsumes ( 'multipart/form-data' )
177- @ApiBody ( {
178- description : 'Upload Song' ,
179- type : UploadSongResponseDto ,
180- } )
332+ @ApiBody ( { description : 'Upload Song' , type : UploadSongResponseDto } )
181333 @UseInterceptors ( FileInterceptor ( 'file' , SongController . multerConfig ) )
182334 @ApiOperation ( {
183335 summary : 'Upload a .nbs file and send the song data, creating a new song' ,
0 commit comments