- Agrega @pinia/nuxt, idb y store central (stores/music.ts) - Cacheo manual desde menú contextual y borrado (TrackContextMenu) - Ícono verde para canciones cacheadas, sin auto-cache al reproducir - Toasts de feedback (stores/toast.ts, ToastContainer) - Fallback offline de listado a IndexedDB; fix MUSIC_DIR absoluto en preview/prod - Ajustes PWA: navigateFallback '/', devOptions, workbox condicional - Estilos y animación del context menu (tema light/dark, blur fuerte) - Correcciones de sintaxis y posicionamiento exacto al cursor
62 lines
2.0 KiB
TypeScript
62 lines
2.0 KiB
TypeScript
import { promises as fs } from 'fs'
|
|
import { join } from 'path'
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
try {
|
|
// Log incoming request for debugging proxy issues
|
|
const headers = getHeaders(event)
|
|
const realIP = headers['x-real-ip'] || headers['x-forwarded-for'] || 'unknown'
|
|
console.log(`[MUSIC API] Music list request from ${realIP}`)
|
|
|
|
const config = useRuntimeConfig()
|
|
// Prefer absolute dir computed at build time; fallback to env or public path
|
|
const defaultPublicPath = config.public?.musicPath || '/music'
|
|
const publicRel = defaultPublicPath.replace(/^\//, '')
|
|
const musicDir = config.musicDirAbs || process.env.MUSIC_DIR || join(process.cwd(), 'public', publicRel)
|
|
|
|
// Check if music directory exists
|
|
try {
|
|
await fs.access(musicDir)
|
|
} catch {
|
|
// Create music directory if it doesn't exist
|
|
await fs.mkdir(musicDir, { recursive: true })
|
|
return { tracks: [] }
|
|
}
|
|
|
|
// Read music directory
|
|
const files = await fs.readdir(musicDir)
|
|
|
|
// Filter audio files
|
|
const audioExtensions = ['.mp3', '.wav', '.flac', '.m4a', '.ogg', '.aac']
|
|
const musicFiles = files.filter(file =>
|
|
audioExtensions.some(ext => file.toLowerCase().endsWith(ext))
|
|
)
|
|
|
|
// Get file stats and create track objects
|
|
const tracks = await Promise.all(
|
|
musicFiles.map(async (file) => {
|
|
const filePath = join(musicDir, file)
|
|
const stats = await fs.stat(filePath)
|
|
|
|
return {
|
|
name: file,
|
|
size: stats.size,
|
|
modified: stats.mtime,
|
|
duration: null // We'll set this on the client side when the audio loads
|
|
}
|
|
})
|
|
)
|
|
|
|
// Sort by name
|
|
tracks.sort((a, b) => a.name.localeCompare(b.name))
|
|
|
|
return { tracks }
|
|
} catch (error) {
|
|
console.error('Error reading music directory:', error)
|
|
throw createError({
|
|
statusCode: 500,
|
|
statusMessage: 'Failed to load music files'
|
|
})
|
|
}
|
|
})
|