diff --git a/packages/backend/src/services/xmlService.ts b/packages/backend/src/services/xmlService.ts
index 5ce6e3d..be0037a 100644
--- a/packages/backend/src/services/xmlService.ts
+++ b/packages/backend/src/services/xmlService.ts
@@ -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 ');
} else {
- const trackCount = node.tracks ? node.tracks.length : 0;
- res.write(`\n `);
-
- if (node.tracks && node.tracks.length > 0) {
- for (const trackId of node.tracks) {
+ const effectiveIds = getEffectiveTrackIds(node);
+ res.write(`\n `);
+ if (effectiveIds.length > 0) {
+ for (const trackId of effectiveIds) {
res.write(`\n `);
}
}
-
res.write('\n ');
}
};
@@ -138,15 +146,13 @@ const streamPlaylistNodeCompact = async (res: any, node: any) => {
res.write('');
} else {
- const trackCount = node.tracks ? node.tracks.length : 0;
- res.write(``);
-
- if (node.tracks && node.tracks.length > 0) {
- for (const trackId of node.tracks) {
+ const effectiveIds = getEffectiveTrackIds(node);
+ res.write(``);
+ if (effectiveIds.length > 0) {
+ for (const trackId of effectiveIds) {
res.write(``);
}
}
-
res.write('');
}
};
@@ -167,15 +173,13 @@ const streamPlaylistNode = async (res: any, node: any, indent: number) => {
res.write(`${spaces}\n`);
} else {
- const trackCount = node.tracks ? node.tracks.length : 0;
- res.write(`${spaces}\n`);
-
- if (node.tracks && node.tracks.length > 0) {
- for (const trackId of node.tracks) {
+ const effectiveIds = getEffectiveTrackIds(node);
+ res.write(`${spaces}\n`);
+ if (effectiveIds.length > 0) {
+ for (const trackId of effectiveIds) {
res.write(`${spaces} \n`);
}
}
-
res.write(`${spaces}\n`);
}
};