All checks were successful
Build and Push Docker Image / docker (push) Successful in 5s
69 lines
2.6 KiB
JavaScript
69 lines
2.6 KiB
JavaScript
import fs from 'fs';
|
|
import path from 'path';
|
|
import { parseFile as mmParseFile } from 'music-metadata';
|
|
import { getPlaylistDir } from './config.js';
|
|
import { loadYearsIndex } from './years.js';
|
|
|
|
/**
|
|
* List tracks from a specific playlist
|
|
* @param {string} [playlistId='default'] - The playlist ID to list tracks from
|
|
* @returns {Promise<Array>} Array of track objects
|
|
*/
|
|
export async function listTracks(playlistId = 'default') {
|
|
const years = loadYearsIndex(playlistId);
|
|
const targetDir = getPlaylistDir(playlistId);
|
|
|
|
const files = fs
|
|
.readdirSync(targetDir)
|
|
.filter((f) => /\.(mp3|wav|m4a|ogg|opus)$/i.test(f))
|
|
.filter((f) => {
|
|
// If both base.ext and base.opus exist, list only once (prefer .opus)
|
|
const ext = path.extname(f).toLowerCase();
|
|
if (ext === '.opus') return true;
|
|
const opusTwin = f.replace(/\.[^.]+$/i, '.opus');
|
|
return !fs.existsSync(path.join(targetDir, opusTwin));
|
|
});
|
|
const tracks = await Promise.all(
|
|
files.map(async (f) => {
|
|
// Prefer .opus for playback if exists
|
|
const ext = path.extname(f).toLowerCase();
|
|
const opusName = ext === '.opus' ? f : f.replace(/\.[^.]+$/i, '.opus');
|
|
const chosen = fs.existsSync(path.join(targetDir, opusName)) ? opusName : f;
|
|
const fp = path.join(targetDir, chosen);
|
|
|
|
// Get metadata from JSON first (priority), then from audio file as fallback
|
|
const jsonMeta = years[f] || years[chosen];
|
|
let year = jsonMeta?.year ?? null;
|
|
let title = jsonMeta?.title ?? path.parse(f).name;
|
|
let artist = jsonMeta?.artist ?? '';
|
|
|
|
// Only parse audio file if JSON doesn't have title or artist metadata
|
|
// Note: year is ONLY taken from years.json, never from audio file
|
|
if (!jsonMeta || !jsonMeta.title || !jsonMeta.artist) {
|
|
try {
|
|
const meta = await mmParseFile(fp, { duration: false });
|
|
title = jsonMeta?.title || meta.common.title || title;
|
|
artist = jsonMeta?.artist || meta.common.artist || artist;
|
|
// year remains from years.json only - do not use meta.common.year
|
|
} catch {}
|
|
}
|
|
|
|
return { id: chosen, file: chosen, title, artist, year };
|
|
})
|
|
);
|
|
return tracks;
|
|
}
|
|
|
|
export function registerTracksApi(app) {
|
|
app.get('/api/tracks', async (req, res) => {
|
|
try {
|
|
// Support optional playlist parameter (defaults to 'default')
|
|
const playlistId = req.query.playlist || 'default';
|
|
const tracks = await listTracks(playlistId);
|
|
res.json({ tracks, playlist: playlistId });
|
|
} catch (e) {
|
|
res.status(500).json({ error: e.message });
|
|
}
|
|
});
|
|
}
|