Geert Rademakes 050e31288a chore: Remove debug endpoint after playlist issue resolution
- Remove /api/songs/debug/playlist/:name endpoint
- Debug endpoint was temporary and no longer needed
- Playlist loading issue resolved by database reload
- Clean up code after successful troubleshooting
2025-08-07 08:57:10 +02:00

248 lines
7.8 KiB
TypeScript

import express, { Request, Response } from 'express';
import { Song } from '../models/Song.js';
import { Playlist } from '../models/Playlist.js';
const router = express.Router();
// Get songs with pagination and search
router.get('/', async (req: Request, res: Response) => {
try {
const page = parseInt(req.query.page as string) || 1;
const limit = parseInt(req.query.limit as string) || 50;
const search = req.query.search as string || '';
const skip = (page - 1) * limit;
console.log(`Fetching songs from database... Page: ${page}, Limit: ${limit}, Search: "${search}"`);
// Build query for search
let query = {};
if (search) {
query = {
$or: [
{ title: { $regex: search, $options: 'i' } },
{ artist: { $regex: search, $options: 'i' } },
{ album: { $regex: search, $options: 'i' } },
{ genre: { $regex: search, $options: 'i' } }
]
};
}
// Get total count for pagination
const totalSongs = await Song.countDocuments(query);
const totalPages = Math.ceil(totalSongs / limit);
// Get songs with pagination
const songs = await Song.find(query)
.sort({ title: 1 })
.skip(skip)
.limit(limit)
.populate('s3File.musicFileId')
.lean();
console.log(`Found ${songs.length} songs (${totalSongs} total), ${songs.filter((s: any) => s.s3File?.hasS3File).length} with S3 files`);
res.json({
songs,
pagination: {
page,
limit,
totalSongs,
totalPages,
hasNextPage: page < totalPages,
hasPrevPage: page > 1
}
});
} catch (error) {
console.error('Error fetching songs:', error);
res.status(500).json({ message: 'Error fetching songs', error });
}
});
// Get songs by playlist with pagination
router.get('/playlist/*', async (req: Request, res: Response) => {
try {
const playlistName = decodeURIComponent(req.params[0]);
const page = parseInt(req.query.page as string) || 1;
const limit = parseInt(req.query.limit as string) || 50;
const search = req.query.search as string || '';
const skip = (page - 1) * limit;
console.log(`Fetching songs for playlist "${playlistName}"... Page: ${page}, Limit: ${limit}, Search: "${search}"`);
// Find the playlist recursively in the playlist structure
const findPlaylistRecursively = (nodes: any[], targetName: string): any => {
for (const node of nodes) {
if (node.name === targetName) {
return node;
}
if (node.children && node.children.length > 0) {
const found = findPlaylistRecursively(node.children, targetName);
if (found) return found;
}
}
return null;
};
// Get all playlists and search recursively
const allPlaylists = await Playlist.find({});
let playlist = null;
for (const playlistDoc of allPlaylists) {
playlist = findPlaylistRecursively([playlistDoc], playlistName);
if (playlist) break;
}
if (!playlist) {
console.log(`Playlist "${playlistName}" not found. Available playlists:`);
const allPlaylistNames = await Playlist.find({}, 'name');
console.log(allPlaylistNames.map(p => p.name));
return res.status(404).json({ message: `Playlist "${playlistName}" not found` });
}
// Get all track IDs from the playlist (including nested playlists)
const getAllTrackIds = (node: any): string[] => {
if (node.type === 'playlist' && node.tracks) {
return node.tracks;
}
if (node.type === 'folder' && node.children) {
return node.children.flatMap((child: any) => getAllTrackIds(child));
}
return [];
};
const trackIds = getAllTrackIds(playlist);
console.log(`Found ${trackIds.length} tracks in playlist "${playlistName}"`);
if (trackIds.length === 0) {
return res.json({
songs: [],
pagination: {
page,
limit,
totalSongs: 0,
totalPages: 0,
hasNextPage: false,
hasPrevPage: false
}
});
}
// Build query for songs in playlist
let query: any = { id: { $in: trackIds } };
if (search) {
query = {
$and: [
{ id: { $in: trackIds } },
{
$or: [
{ title: { $regex: search, $options: 'i' } },
{ artist: { $regex: search, $options: 'i' } },
{ album: { $regex: search, $options: 'i' } },
{ genre: { $regex: search, $options: 'i' } }
]
}
]
};
}
// Get total count for pagination
const totalSongs = await Song.countDocuments(query);
const totalPages = Math.ceil(totalSongs / limit);
// Calculate total duration for the entire playlist
const allPlaylistSongs = await Song.find({ id: { $in: trackIds } }).lean();
const totalDuration = allPlaylistSongs.reduce((total, song: any) => {
if (!song.totalTime) return total;
const totalTimeStr = String(song.totalTime);
const seconds = Math.floor(Number(totalTimeStr) / (totalTimeStr.length > 4 ? 1000 : 1));
return total + seconds;
}, 0);
// Get songs with pagination
const songs = await Song.find(query)
.sort({ title: 1 })
.skip(skip)
.limit(limit)
.populate('s3File.musicFileId')
.lean();
console.log(`Found ${songs.length} songs for playlist "${playlistName}" (${totalSongs} total), ${songs.filter((s: any) => s.s3File?.hasS3File).length} with S3 files`);
res.json({
songs,
pagination: {
page,
limit,
totalSongs,
totalPages,
hasNextPage: page < totalPages,
hasPrevPage: page > 1
},
totalDuration: totalDuration
});
} catch (error) {
console.error('Error fetching playlist songs:', error);
res.status(500).json({ message: 'Error fetching playlist songs', error });
}
});
// Get total song count
router.get('/count', async (req: Request, res: Response) => {
try {
const count = await Song.countDocuments();
res.json({ count });
} catch (error) {
console.error('Error fetching song count:', error);
res.status(500).json({ message: 'Error fetching song count', error });
}
});
// Export library to XML format with streaming
router.get('/export', async (req: Request, res: Response) => {
try {
console.log('Starting streaming XML export...');
// Set response headers for file download
res.setHeader('Content-Type', 'application/xml');
res.setHeader('Content-Disposition', `attachment; filename="rekordbox-library-${new Date().toISOString().split('T')[0]}.xml"`);
res.setHeader('Transfer-Encoding', 'chunked');
// Import the streaming XML generation function
const { streamToXml } = await import('../services/xmlService.js');
// Stream XML generation to response
await streamToXml(res);
console.log('Streaming XML export completed successfully');
} catch (error) {
console.error('Error exporting library:', error);
if (!res.headersSent) {
res.status(500).json({ message: 'Error exporting library', error });
}
}
});
// Create multiple songs
router.post('/batch', async (req: Request, res: Response) => {
try {
console.log('Received batch upload request');
const songs = req.body;
console.log(`Attempting to save ${songs.length} songs`);
// Delete all existing songs first
await Song.deleteMany({});
console.log('Cleared existing songs');
// Insert new songs
const result = await Song.insertMany(songs);
console.log(`Successfully saved ${result.length} songs`);
res.status(201).json(result);
} catch (error) {
console.error('Error creating songs:', error);
res.status(500).json({ message: 'Error creating songs', error });
}
});
export const songsRouter = router;