feat: add album routes, playlist detail, liked tracks, fix search/artist routing
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
import mongoose from 'mongoose';
|
||||
import Track from '../models/Track.js';
|
||||
import Album from '../models/Album.js';
|
||||
|
||||
export const albumController = {
|
||||
async list(req, res, next) {
|
||||
try {
|
||||
const albums = await Album.find()
|
||||
.sort({ createdAt: -1 })
|
||||
.populate('tracks')
|
||||
.lean();
|
||||
res.json({ success: true, data: albums });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
|
||||
async getById(req, res, next) {
|
||||
try {
|
||||
const { albumId } = req.params;
|
||||
const decodedId = decodeURIComponent(albumId);
|
||||
|
||||
if (mongoose.Types.ObjectId.isValid(decodedId)) {
|
||||
const album = await Album.findById(decodedId)
|
||||
.populate('tracks')
|
||||
.lean();
|
||||
if (album) {
|
||||
return res.json({ success: true, data: album });
|
||||
}
|
||||
}
|
||||
|
||||
const tracks = await Track.find({ album: decodedId })
|
||||
.sort({ createdAt: -1 })
|
||||
.lean();
|
||||
|
||||
if (!tracks.length) {
|
||||
return res.status(404).json({ success: false, message: 'Album not found' });
|
||||
}
|
||||
|
||||
const data = {
|
||||
_id: decodedId,
|
||||
title: decodedId,
|
||||
artist: tracks[0].artist,
|
||||
coverUrl: tracks.find(t => t.coverUrl)?.coverUrl || null,
|
||||
releaseYear: null,
|
||||
tracks,
|
||||
};
|
||||
|
||||
return res.json({ success: true, data });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
|
||||
async getByName(req, res, next) {
|
||||
try {
|
||||
const { albumTitle } = req.params;
|
||||
const decoded = decodeURIComponent(albumTitle);
|
||||
|
||||
const tracks = await Track.find({ album: decoded })
|
||||
.sort({ createdAt: -1 })
|
||||
.lean();
|
||||
|
||||
if (!tracks.length) {
|
||||
return res.status(404).json({ success: false, message: 'Album not found' });
|
||||
}
|
||||
|
||||
const data = {
|
||||
title: decoded,
|
||||
artist: tracks[0].artist,
|
||||
coverUrl: tracks.find(t => t.coverUrl)?.coverUrl || null,
|
||||
tracks,
|
||||
trackCount: tracks.length,
|
||||
};
|
||||
|
||||
return res.json({ success: true, data });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
};
|
||||
@@ -13,6 +13,28 @@ export const playlistController = {
|
||||
}
|
||||
},
|
||||
|
||||
async getById(req, res, next) {
|
||||
try {
|
||||
const { playlistId } = req.params;
|
||||
const playlist = await Playlist.findOne({
|
||||
_id: playlistId,
|
||||
user: req.userId,
|
||||
})
|
||||
.populate('tracks')
|
||||
.lean();
|
||||
|
||||
if (!playlist) {
|
||||
return res
|
||||
.status(404)
|
||||
.json({ success: false, message: 'Playlist not found' });
|
||||
}
|
||||
|
||||
return res.json({ success: true, data: playlist });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
|
||||
async create(req, res, next) {
|
||||
try {
|
||||
const { name, description } = req.body;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import mongoose from 'mongoose';
|
||||
import Track from '../models/Track.js';
|
||||
|
||||
export const searchController = {
|
||||
@@ -26,11 +27,42 @@ export const searchController = {
|
||||
.limit(50)
|
||||
.lean();
|
||||
|
||||
const artistNames = [...new Set(tracks.map(t => t.artist))];
|
||||
const artists = artistNames.map(name => ({ name }));
|
||||
const artistMap = new Map();
|
||||
tracks.forEach(t => {
|
||||
if (!artistMap.has(t.artist)) {
|
||||
artistMap.set(t.artist, {
|
||||
_id: new mongoose.Types.ObjectId(),
|
||||
name: t.artist,
|
||||
imageUrl: t.coverUrl || '',
|
||||
trackCount: 0,
|
||||
});
|
||||
}
|
||||
const entry = artistMap.get(t.artist);
|
||||
entry.trackCount++;
|
||||
if (t.coverUrl && !entry.imageUrl) {
|
||||
entry.imageUrl = t.coverUrl;
|
||||
}
|
||||
});
|
||||
const artists = [...artistMap.values()];
|
||||
|
||||
const albumNames = [...new Set(tracks.map(t => t.album))];
|
||||
const albums = albumNames.map(name => ({ name }));
|
||||
const albumMap = new Map();
|
||||
tracks.forEach(t => {
|
||||
if (!albumMap.has(t.album)) {
|
||||
albumMap.set(t.album, {
|
||||
_id: new mongoose.Types.ObjectId(),
|
||||
title: t.album,
|
||||
artist: t.artist,
|
||||
coverUrl: t.coverUrl || '',
|
||||
trackCount: 0,
|
||||
});
|
||||
}
|
||||
const entry = albumMap.get(t.album);
|
||||
entry.trackCount++;
|
||||
if (t.coverUrl && !entry.coverUrl) {
|
||||
entry.coverUrl = t.coverUrl;
|
||||
}
|
||||
});
|
||||
const albums = [...albumMap.values()];
|
||||
|
||||
const data = { tracks, artists, albums };
|
||||
|
||||
|
||||
@@ -11,6 +11,25 @@ export const trackController = {
|
||||
}
|
||||
},
|
||||
|
||||
async listLiked(req, res, next) {
|
||||
try {
|
||||
const user = await User.findById(req.userId)
|
||||
.select('likedTracks')
|
||||
.populate('likedTracks')
|
||||
.lean();
|
||||
|
||||
if (!user) {
|
||||
return res
|
||||
.status(404)
|
||||
.json({ success: false, message: 'User not found' });
|
||||
}
|
||||
|
||||
return res.json({ success: true, data: user.likedTracks || [] });
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
},
|
||||
|
||||
async like(req, res, next) {
|
||||
try {
|
||||
const { trackId } = req.params;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Router } from 'express';
|
||||
import { albumController } from '../controllers/albumController.js';
|
||||
|
||||
const router = Router();
|
||||
|
||||
router.get('/', albumController.list);
|
||||
router.get('/:albumId', albumController.getById);
|
||||
router.get('/title/:albumTitle', albumController.getByName);
|
||||
|
||||
export default router;
|
||||
@@ -4,6 +4,7 @@ import adminRoutes from './admins.js';
|
||||
import trackRoutes from './tracks.js';
|
||||
import playlistRoutes from './playlists.js';
|
||||
import artistRoutes from './artists.js';
|
||||
import albumRoutes from './albums.js';
|
||||
import searchRoutes from './search.js';
|
||||
|
||||
const router = Router();
|
||||
@@ -23,6 +24,7 @@ router.use('/admin', adminRoutes);
|
||||
router.use('/tracks', trackRoutes);
|
||||
router.use('/playlists', playlistRoutes);
|
||||
router.use('/artists', artistRoutes);
|
||||
router.use('/albums', albumRoutes);
|
||||
router.use('/search', searchRoutes);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -6,6 +6,7 @@ import { uploadPlaylistCover } from '../utils/multer.js';
|
||||
const router = Router();
|
||||
|
||||
router.get('/', auth, playlistController.list);
|
||||
router.get('/:playlistId', auth, playlistController.getById);
|
||||
router.post(
|
||||
'/',
|
||||
auth,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { auth } from '../middlewares/auth.js';
|
||||
const router = Router();
|
||||
|
||||
router.get('/', trackController.list);
|
||||
router.get('/liked', auth, trackController.listLiked);
|
||||
router.post('/:trackId/like', auth, trackController.like);
|
||||
router.delete('/:trackId/like', auth, trackController.unlike);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user