added inintla stuff
This commit is contained in:
105
backend/server.js
Normal file
105
backend/server.js
Normal file
@@ -0,0 +1,105 @@
|
||||
// server.js
|
||||
const express = require('express');
|
||||
const path = require('path');
|
||||
const fs = require('fs/promises');
|
||||
const mm = require('music-metadata');
|
||||
const cors = require('cors');
|
||||
|
||||
const app = express();
|
||||
app.use(cors()); // adjust for production if needed
|
||||
|
||||
// Base music directory
|
||||
const MUSIC_DIR = path.resolve(__dirname, 'music');
|
||||
const SUPPORTED = ['.flac', '.mp3', '.wav', '.m4a', '.ogg'];
|
||||
|
||||
// Serve static music files
|
||||
app.use('/music', express.static(MUSIC_DIR, {
|
||||
setHeaders: (res) => {
|
||||
res.setHeader('Cache-Control', 'public, max-age=86400');
|
||||
}
|
||||
}));
|
||||
|
||||
// Recursive scan for all music files
|
||||
async function scanDir(dir) {
|
||||
let results = [];
|
||||
const files = await fs.readdir(dir, { withFileTypes: true });
|
||||
|
||||
for (const file of files) {
|
||||
const fullPath = path.join(dir, file.name);
|
||||
if (file.isDirectory()) {
|
||||
results = results.concat(await scanDir(fullPath));
|
||||
} else if (SUPPORTED.includes(path.extname(file.name).toLowerCase())) {
|
||||
results.push(fullPath);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
// Parse filename into metadata
|
||||
function parseFilename(filename) {
|
||||
// Expected: Title__Movie__Genre__YYYY-MM-DD.ext
|
||||
const base = path.basename(filename);
|
||||
const noExt = base.replace(/\.[^.]+$/, '');
|
||||
const parts = noExt.split('__');
|
||||
|
||||
return {
|
||||
title: parts[0] || 'Unknown',
|
||||
movie: parts[1] && parts[1] !== 'None' ? parts[1] : null,
|
||||
genre: parts[2] || null,
|
||||
date: parts[3] || null,
|
||||
filename: base,
|
||||
};
|
||||
}
|
||||
|
||||
// API endpoint: list all tracks
|
||||
app.get('/api/tracks', async (req, res) => {
|
||||
try {
|
||||
const files = await scanDir(MUSIC_DIR);
|
||||
const tracks = [];
|
||||
|
||||
for (const f of files) {
|
||||
const stat = await fs.stat(f);
|
||||
const metadata = parseFilename(f);
|
||||
|
||||
metadata.path = `/music/${encodeURIComponent(path.relative(MUSIC_DIR, f))}`;
|
||||
metadata.size = stat.size;
|
||||
metadata.ext = path.extname(f).toLowerCase();
|
||||
metadata.duration = null;
|
||||
metadata.tags = {};
|
||||
|
||||
// Try reading embedded metadata (optional)
|
||||
try {
|
||||
const mmData = await mm.parseFile(f, { duration: true });
|
||||
if (mmData.format.duration) metadata.duration = Math.round(mmData.format.duration);
|
||||
if (mmData.common) {
|
||||
metadata.tags = mmData.common;
|
||||
metadata.title = mmData.common.title || metadata.title;
|
||||
metadata.movie = mmData.common.album || metadata.movie;
|
||||
if (!metadata.genre && mmData.common.genre) {
|
||||
metadata.genre = Array.isArray(mmData.common.genre) ? mmData.common.genre.join(', ') : mmData.common.genre;
|
||||
}
|
||||
if (!metadata.date && mmData.common.date) metadata.date = mmData.common.date;
|
||||
}
|
||||
} catch (err) {
|
||||
// ignore metadata errors
|
||||
}
|
||||
|
||||
tracks.push(metadata);
|
||||
}
|
||||
|
||||
// Sort: first by date (desc), then by filename
|
||||
tracks.sort((a, b) => {
|
||||
if (a.date && b.date) return String(b.date).localeCompare(String(a.date));
|
||||
return a.filename.localeCompare(b.filename);
|
||||
});
|
||||
|
||||
res.json({ tracks });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).json({ error: 'Failed to read music folder' });
|
||||
}
|
||||
});
|
||||
|
||||
// Start server
|
||||
const PORT = process.env.PORT || 3001;
|
||||
app.listen(PORT, () => console.log(`Music API server running at http://localhost:${PORT}`));
|
||||
Reference in New Issue
Block a user