karaoke-player-nextjs
v1.1.2
Published
Next.js/ES Modules version of karaoke-player library for reading and playing MIDI/KAR karaoke files
Downloads
49
Maintainers
Readme
Karaoke Player - Next.js/ES Modules/TypeScript Version
Next.js compatible version of karaoke-player library with server/client separation for optimal performance.
✨ v1.2.0 - Now with TypeScript!
Features
- ✅ Full TypeScript support with type definitions
- ✅ Next.js App Router compatible (Server & Client Components)
- ✅ ES Modules (ESM) first
- ✅ Server/Client separation for optimal performance
- ✅ Browser and Node.js compatible
- ✅ Supports Thai encoding (TIS-620, Windows-874)
- ✅ MIDI/KAR file parsing and playback
- ✅ Zero configuration - works out of the box
What's New in 1.2.0
- 🎉 Full TypeScript Support: Type definitions for all exports
- 🐛 Fixed Next.js Compatibility: Resolved Buffer API issues in browser environments
- 📦 Better DX: Autocomplete and type checking in your IDE
- 🔧 Improved Stability: Environment detection and proper fallbacks
Architecture
- Server-side: KAR file reading and parsing (uses Node.js
fs,path) - Client-side: MIDI playback (uses Web Audio API)
- API: Communication layer between server and client
Installation
npm install karaoke-player-nextjsQuick Start
1. Create API Routes (Server-side)
Create API routes in your Next.js project:
app/api/karaoke/lyrics/route.js:
import { handleLyricsRequest } from 'karaoke-player-nextjs/api/server';
export async function POST(request) {
return handleLyricsRequest(request);
}app/api/karaoke/info/route.js:
import { handleFileInfoRequest } from 'karaoke-player-nextjs/api/server';
export async function POST(request) {
return handleFileInfoRequest(request);
}2. Use in Client Component
app/components/KaraokePlayer.js:
'use client';
import { useState } from 'react';
import { Player } from 'karaoke-player-nextjs/client';
import { fetchLyrics } from 'karaoke-player-nextjs/api/client';
export default function KaraokePlayer() {
const [lyrics, setLyrics] = useState([]);
const [player, setPlayer] = useState(null);
const handleFileSelect = async (e) => {
const file = e.target.files[0];
if (!file) return;
// Fetch lyrics from server API
try {
const data = await fetchLyrics(file);
setLyrics(data.lyrics);
} catch (error) {
console.error('Failed to fetch lyrics:', error);
}
// Initialize player (browser-only)
if (typeof window !== 'undefined' && window.MIDIPlayer) {
const p = new Player(e.target, (song) => {
console.log('Song loaded:', song);
});
setPlayer(p);
}
};
return (
<div>
<input type="file" onChange={handleFileSelect} accept=".mid,.midi,.kar" />
<div>
{lyrics.map((line, i) => (
<p key={i}>{line.text}</p>
))}
</div>
</div>
);
}API Reference
Server-side (karaoke-player-nextjs/server)
Use in API routes, Server Components, or server-side code:
import {
KarFile,
MIDIFile,
TextEncoding,
getLyricsFromBuffer,
getTextFromBuffer,
getEventsFromBuffer,
getInfoFromBuffer
} from 'karaoke-player-nextjs/server';
// Parse buffer (works in both server and client)
const buffer = await file.arrayBuffer();
const lyrics = getLyricsFromBuffer(buffer, 'song.kar');Functions:
getLyricsFromBuffer(buffer, fileName)- Get lyrics from buffergetTextFromBuffer(buffer, fileName)- Get raw text from buffergetEventsFromBuffer(buffer, fileName)- Get MIDI events from buffergetInfoFromBuffer(buffer, fileName)- Get file information from bufferreadFile(filePath, callback)- Read file from disk (server-only)readBuffer(buffer, fileName)- Read from buffer
Client-side (karaoke-player-nextjs/client)
Use in Client Components with "use client" directive:
'use client';
import { Player } from 'karaoke-player-nextjs/client';
const player = new Player('fileInputId', (song) => {
console.log('Song loaded:', song);
});
player.play();
player.pause();
player.stop();Functions:
Player- Browser-only MIDI player classcreatePlayer(fileInput, onLoad)- Create player instanceisBrowser()- Check if running in browser
API Client Helpers (karaoke-player-nextjs/api/client)
Functions to communicate with server API routes:
import {
fetchLyrics,
fetchFileInfo,
fetchEvents,
fetchText
} from 'karaoke-player-nextjs/api/client';
// Fetch lyrics from server
const data = await fetchLyrics(file);
console.log(data.lyrics);
// Fetch file info
const info = await fetchFileInfo(file);
console.log(info);Functions:
fetchLyrics(file, apiRoute?)- Fetch lyrics from APIfetchFileInfo(file, apiRoute?)- Fetch file info from APIfetchEvents(file, apiRoute?)- Fetch events from APIfetchText(file, apiRoute?)- Fetch text from API
API Server Helpers (karaoke-player-nextjs/api/server)
Use in Next.js API routes:
import {
handleLyricsRequest,
handleFileInfoRequest,
handleEventsRequest,
handleTextRequest,
parseUploadedFile
} from 'karaoke-player-nextjs/api/server';
// In API route
export async function POST(request) {
return handleLyricsRequest(request);
}Functions:
handleLyricsRequest(request)- Handle lyrics API requesthandleFileInfoRequest(request)- Handle file info API requesthandleEventsRequest(request)- Handle events API requesthandleTextRequest(request)- Handle text API requestparseUploadedFile(formData)- Parse uploaded file from FormData
Complete Example
Server API Route
app/api/karaoke/lyrics/route.js:
import { handleLyricsRequest } from 'karaoke-player-nextjs/api/server';
export async function POST(request) {
return handleLyricsRequest(request);
}Client Component
app/components/KaraokePlayer.js:
'use client';
import { useState, useRef, useEffect } from 'react';
import { Player } from 'karaoke-player-nextjs/client';
import { fetchLyrics } from 'karaoke-player-nextjs/api/client';
export default function KaraokePlayer() {
const [lyrics, setLyrics] = useState([]);
const [player, setPlayer] = useState(null);
const fileInputRef = useRef(null);
useEffect(() => {
// Load MIDIPlayer script dynamically
if (typeof window !== 'undefined' && !window.MIDIPlayer) {
const script = document.createElement('script');
script.src = '/path/to/MIDIPlayer.js';
script.onload = () => {
if (fileInputRef.current) {
const p = new Player(fileInputRef.current, (song) => {
console.log('Song loaded:', song);
});
setPlayer(p);
}
};
document.head.appendChild(script);
}
}, []);
const handleFileSelect = async (e) => {
const file = e.target.files[0];
if (!file) return;
// Fetch lyrics from server API
try {
const data = await fetchLyrics(file);
setLyrics(data.lyrics);
} catch (error) {
console.error('Failed to fetch lyrics:', error);
alert('Failed to load lyrics: ' + error.message);
}
};
return (
<div>
<input
ref={fileInputRef}
type="file"
onChange={handleFileSelect}
accept=".mid,.midi,.kar"
/>
<div>
<button onClick={() => player?.play()}>Play</button>
<button onClick={() => player?.pause()}>Pause</button>
<button onClick={() => player?.stop()}>Stop</button>
</div>
<div>
<h2>Lyrics</h2>
{lyrics.map((line, i) => (
<p key={i} data-time={line.time}>
{line.text}
</p>
))}
</div>
</div>
);
}Benefits of Server/Client Separation
- Smaller Client Bundle: KAR parsing code stays on server
- Better Performance: Heavy parsing happens server-side
- Security: File processing happens on server
- Scalability: Can cache parsed data on server
- SEO Friendly: Lyrics can be server-rendered
Notes
- Server modules require Node.js environment
- Client modules require browser environment with Web Audio API
- Player requires
MIDIPlayer.jsscript to be loaded - Use
"use client"directive for Client Components - API routes work automatically with Next.js App Router
License
MIT
