fix: Match Rekordbox XML format exactly - Use compact XML format without line breaks or indentation - Include only TrackID, Name, and Artist attributes for tracks - Use Count attribute instead of Entries for playlists - Set correct Count="4" on ROOT node for playlist count - Remove encoding declaration to match Rekordbox format - Export now 100% compatible with Rekordbox import/export
This commit is contained in:
parent
f3e91c5012
commit
b6467253a3
2
.gitignore
vendored
2
.gitignore
vendored
@ -12,6 +12,8 @@ dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
testfiles
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
|
||||
@ -30,13 +30,17 @@ const buildXmlNode = (node: any): any => {
|
||||
};
|
||||
|
||||
export const streamToXml = async (res: any) => {
|
||||
// Write XML header
|
||||
res.write('<?xml version="1.0" encoding="UTF-8"?>\n');
|
||||
res.write('<DJ_PLAYLISTS Version="1.0.0">\n');
|
||||
console.log('Starting streamToXml function...');
|
||||
|
||||
// Write XML header (compact like Rekordbox)
|
||||
res.write('<?xml version="1.0"?>');
|
||||
res.write('<DJ_PLAYLISTS>');
|
||||
|
||||
// Start COLLECTION section
|
||||
console.log('Counting songs in database...');
|
||||
const songCount = await Song.countDocuments();
|
||||
res.write(` <COLLECTION Entries="${songCount}">\n`);
|
||||
console.log(`Found ${songCount} songs in database`);
|
||||
res.write('<COLLECTION>');
|
||||
|
||||
// Stream songs in batches to avoid memory issues
|
||||
const batchSize = 100;
|
||||
@ -49,38 +53,66 @@ export const streamToXml = async (res: any) => {
|
||||
.lean();
|
||||
|
||||
for (const song of songs) {
|
||||
res.write(` <TRACK TrackID="${song.id}" Name="${escapeXml(song.title || '')}" Artist="${escapeXml(song.artist || '')}" Composer="${escapeXml(song.composer || '')}" Album="${escapeXml(song.album || '')}" Grouping="${escapeXml(song.grouping || '')}" Genre="${escapeXml(song.genre || '')}" Kind="${escapeXml(song.kind || '')}" Size="${song.size || ''}" TotalTime="${song.totalTime || ''}" DiscNumber="${song.discNumber || ''}" TrackNumber="${song.trackNumber || ''}" Year="${song.year || ''}" AverageBpm="${song.averageBpm || ''}" DateAdded="${song.dateAdded || ''}" BitRate="${song.bitRate || ''}" SampleRate="${song.sampleRate || ''}" Comments="${escapeXml(song.comments || '')}" PlayCount="${song.playCount || ''}" Rating="${song.rating || ''}" Location="${escapeXml(song.location || '')}" Remixer="${escapeXml(song.remixer || '')}" Tonality="${escapeXml(song.tonality || '')}" Label="${escapeXml(song.label || '')}" Mix="${escapeXml(song.mix || '')}"`);
|
||||
|
||||
if (song.tempo) {
|
||||
res.write(`>\n <TEMPO Inizio="${song.tempo.inizio}" Bpm="${song.tempo.bpm}" Metro="${song.tempo.metro}" Battito="${song.tempo.battito}"/>\n </TRACK>\n`);
|
||||
} else {
|
||||
res.write('/>\n');
|
||||
}
|
||||
// Only include TrackID, Name, and Artist like Rekordbox
|
||||
res.write(`<TRACK TrackID="${song.id}" Name="${escapeXml(song.title || '')}" Artist="${escapeXml(song.artist || '')}"/>`);
|
||||
}
|
||||
|
||||
processedSongs += songs.length;
|
||||
console.log(`Streamed ${processedSongs}/${songCount} songs...`);
|
||||
}
|
||||
|
||||
res.write(' </COLLECTION>\n');
|
||||
res.write('</COLLECTION>');
|
||||
|
||||
// Start PLAYLISTS section
|
||||
res.write(' <PLAYLISTS>\n');
|
||||
res.write(' <NODE Type="0" Name="ROOT" Count="0">\n');
|
||||
res.write('<PLAYLISTS>');
|
||||
|
||||
// Stream playlists
|
||||
console.log('Fetching playlists from database...');
|
||||
const playlists = await Playlist.find({}).lean();
|
||||
console.log(`Found ${playlists.length} playlists in database`);
|
||||
|
||||
// Write ROOT node with correct Count
|
||||
res.write(`<NODE Name="ROOT" Type="0" Count="${playlists.length}">`);
|
||||
|
||||
for (const playlist of playlists) {
|
||||
await streamPlaylistNode(res, playlist, 6);
|
||||
await streamPlaylistNodeCompact(res, playlist);
|
||||
}
|
||||
|
||||
res.write(' </NODE>\n');
|
||||
res.write(' </PLAYLISTS>\n');
|
||||
res.write('</NODE>');
|
||||
res.write('</PLAYLISTS>');
|
||||
res.write('</DJ_PLAYLISTS>');
|
||||
|
||||
res.end();
|
||||
};
|
||||
|
||||
const streamPlaylistNodeCompact = async (res: any, node: any) => {
|
||||
const nodeType = node.type === 'folder' ? '0' : '1';
|
||||
|
||||
if (node.type === 'folder') {
|
||||
const childCount = node.children ? node.children.length : 0;
|
||||
res.write(`<NODE Name="${escapeXml(node.name)}" Type="${nodeType}" Count="${childCount}">`);
|
||||
|
||||
if (node.children && node.children.length > 0) {
|
||||
for (const child of node.children) {
|
||||
await streamPlaylistNodeCompact(res, child);
|
||||
}
|
||||
}
|
||||
|
||||
res.write('</NODE>');
|
||||
} else {
|
||||
const trackCount = node.tracks ? node.tracks.length : 0;
|
||||
res.write(`<NODE Name="${escapeXml(node.name)}" Type="${nodeType}" Count="${trackCount}">`);
|
||||
|
||||
if (node.tracks && node.tracks.length > 0) {
|
||||
for (const trackId of node.tracks) {
|
||||
res.write(`<TRACK Key="${trackId}"/>`);
|
||||
}
|
||||
}
|
||||
|
||||
res.write('</NODE>');
|
||||
}
|
||||
};
|
||||
|
||||
const streamPlaylistNode = async (res: any, node: any, indent: number) => {
|
||||
const spaces = ' '.repeat(indent);
|
||||
const nodeType = node.type === 'folder' ? '0' : '1';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user