feat(export): honor custom playlist order overlay when exporting Rekordbox XML (Entries + TRACK sequence)
This commit is contained in:
parent
9249a5a4a7
commit
5659dde540
@ -2,6 +2,17 @@ import { create } from "xmlbuilder";
|
||||
import { Song } from "../models/Song.js";
|
||||
import { Playlist } from "../models/Playlist.js";
|
||||
|
||||
// Compute effective playlist order: custom overlay (order) + remaining base tracks
|
||||
const getEffectiveTrackIds = (node: any): string[] => {
|
||||
const base: string[] = Array.isArray(node?.tracks) ? node.tracks : [];
|
||||
const overlay: string[] = Array.isArray(node?.order) ? node.order : [];
|
||||
if (overlay.length === 0) return base;
|
||||
const setBase = new Set(base);
|
||||
const orderedKnown = overlay.filter((id: string) => setBase.has(id));
|
||||
const missing = base.filter((id: string) => !overlay.includes(id));
|
||||
return [...orderedKnown, ...missing];
|
||||
};
|
||||
|
||||
const buildXmlNode = (node: any): any => {
|
||||
const xmlNode: any = {
|
||||
'@Type': node.type === 'folder' ? '0' : '1',
|
||||
@ -14,13 +25,12 @@ const buildXmlNode = (node: any): any => {
|
||||
xmlNode.NODE = node.children.map((child: any) => buildXmlNode(child));
|
||||
}
|
||||
} else {
|
||||
// For playlists, always include KeyType and Entries
|
||||
// For playlists, include KeyType and Entries based on effective order
|
||||
const effectiveIds = getEffectiveTrackIds(node);
|
||||
xmlNode['@KeyType'] = '0';
|
||||
// Set Entries to the actual number of tracks
|
||||
xmlNode['@Entries'] = (node.tracks || []).length;
|
||||
// Include TRACK elements if there are tracks
|
||||
if (node.tracks && node.tracks.length > 0) {
|
||||
xmlNode.TRACK = node.tracks.map((trackId: string) => ({
|
||||
xmlNode['@Entries'] = effectiveIds.length;
|
||||
if (effectiveIds.length > 0) {
|
||||
xmlNode.TRACK = effectiveIds.map((trackId: string) => ({
|
||||
'@Key': trackId
|
||||
}));
|
||||
}
|
||||
@ -110,15 +120,13 @@ const streamPlaylistNodeFull = async (res: any, node: any) => {
|
||||
|
||||
res.write('\n </NODE>');
|
||||
} else {
|
||||
const trackCount = node.tracks ? node.tracks.length : 0;
|
||||
res.write(`\n <NODE Name="${escapeXml(node.name)}" Type="${nodeType}" KeyType="0" Entries="${trackCount}">`);
|
||||
|
||||
if (node.tracks && node.tracks.length > 0) {
|
||||
for (const trackId of node.tracks) {
|
||||
const effectiveIds = getEffectiveTrackIds(node);
|
||||
res.write(`\n <NODE Name="${escapeXml(node.name)}" Type="${nodeType}" KeyType="0" Entries="${effectiveIds.length}">`);
|
||||
if (effectiveIds.length > 0) {
|
||||
for (const trackId of effectiveIds) {
|
||||
res.write(`\n <TRACK Key="${trackId}"/>`);
|
||||
}
|
||||
}
|
||||
|
||||
res.write('\n </NODE>');
|
||||
}
|
||||
};
|
||||
@ -138,15 +146,13 @@ const streamPlaylistNodeCompact = async (res: any, node: any) => {
|
||||
|
||||
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) {
|
||||
const effectiveIds = getEffectiveTrackIds(node);
|
||||
res.write(`<NODE Name="${escapeXml(node.name)}" Type="${nodeType}" Count="${effectiveIds.length}">`);
|
||||
if (effectiveIds.length > 0) {
|
||||
for (const trackId of effectiveIds) {
|
||||
res.write(`<TRACK Key="${trackId}"/>`);
|
||||
}
|
||||
}
|
||||
|
||||
res.write('</NODE>');
|
||||
}
|
||||
};
|
||||
@ -167,15 +173,13 @@ const streamPlaylistNode = async (res: any, node: any, indent: number) => {
|
||||
|
||||
res.write(`${spaces}</NODE>\n`);
|
||||
} else {
|
||||
const trackCount = node.tracks ? node.tracks.length : 0;
|
||||
res.write(`${spaces}<NODE Type="${nodeType}" Name="${escapeXml(node.name)}" KeyType="0" Entries="${trackCount}">\n`);
|
||||
|
||||
if (node.tracks && node.tracks.length > 0) {
|
||||
for (const trackId of node.tracks) {
|
||||
const effectiveIds = getEffectiveTrackIds(node);
|
||||
res.write(`${spaces}<NODE Type="${nodeType}" Name="${escapeXml(node.name)}" KeyType="0" Entries="${effectiveIds.length}">\n`);
|
||||
if (effectiveIds.length > 0) {
|
||||
for (const trackId of effectiveIds) {
|
||||
res.write(`${spaces} <TRACK Key="${trackId}"/>\n`);
|
||||
}
|
||||
}
|
||||
|
||||
res.write(`${spaces}</NODE>\n`);
|
||||
}
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user