@karaplay/file-coder 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/BROWSER_API.md +318 -0
  2. package/README.md +216 -0
  3. package/REFACTORING_SUMMARY.txt +250 -0
  4. package/WORKFLOW_SUMMARY.txt +78 -0
  5. package/bin/ncntokar-cli.js +39 -0
  6. package/dist/client.d.ts +26 -0
  7. package/dist/client.js +74 -0
  8. package/dist/emk/client-decoder.d.ts +22 -0
  9. package/dist/emk/client-decoder.js +133 -0
  10. package/dist/emk/server-decode.d.ts +21 -0
  11. package/dist/emk/server-decode.js +123 -0
  12. package/dist/emk-to-kar.browser.d.ts +51 -0
  13. package/dist/emk-to-kar.browser.js +139 -0
  14. package/dist/emk-to-kar.d.ts +53 -0
  15. package/dist/emk-to-kar.js +210 -0
  16. package/dist/index.d.ts +12 -0
  17. package/dist/index.js +64 -0
  18. package/dist/kar-reader.browser.d.ts +46 -0
  19. package/dist/kar-reader.browser.js +209 -0
  20. package/dist/kar-reader.d.ts +42 -0
  21. package/dist/kar-reader.js +197 -0
  22. package/dist/ncntokar.browser.d.ts +99 -0
  23. package/dist/ncntokar.browser.js +296 -0
  24. package/dist/ncntokar.d.ts +88 -0
  25. package/dist/ncntokar.js +340 -0
  26. package/examples/NextJSComponent.tsx +175 -0
  27. package/libs/emk/client-decoder.ts +142 -0
  28. package/libs/emk/server-decode.ts +133 -0
  29. package/libs/ncntokar.js +256 -0
  30. package/package.json +79 -0
  31. package/songs/.gitkeep +3 -0
  32. package/songs/cur/BPL3457.cur +0 -0
  33. package/songs/cur/Z2510006.cur +0 -0
  34. package/songs/cur/Z2510008.cur +0 -0
  35. package/songs/cur/Z2510136.cur +0 -0
  36. package/songs/cur/Z2510137.cur +0 -0
  37. package/songs/cur/Z2510138.cur +0 -0
  38. package/songs/cur/Z2510139.cur +0 -0
  39. package/songs/cur/Z2510140.cur +0 -0
  40. package/songs/cur/Z2510141.cur +0 -0
  41. package/songs/cur/song.cur +0 -0
  42. package/songs/emk/Z2510001.emk +0 -0
  43. package/songs/emk/Z2510002.emk +0 -0
  44. package/songs/emk/Z2510003.emk +0 -0
  45. package/songs/emk/Z2510004.emk +0 -0
  46. package/songs/emk/Z2510005.emk +0 -0
  47. package/songs/emk/Z2510006.emk +0 -0
  48. package/songs/kar/bpl3457.kar +0 -0
  49. package/songs/kar/z2510006.kar +0 -0
  50. package/songs/kar/z2510008.kar +0 -0
  51. package/songs/kar/z2510136.kar +0 -0
  52. package/songs/kar/z2510137.kar +0 -0
  53. package/songs/kar/z2510138.kar +0 -0
  54. package/songs/kar/z2510139.kar +0 -0
  55. package/songs/kar/z2510140.kar +0 -0
  56. package/songs/kar/z2510141.kar +0 -0
  57. package/songs/lyr/BPL3457.lyr +57 -0
  58. package/songs/lyr/Z2510006.lyr +53 -0
  59. package/songs/lyr/Z2510008.lyr +57 -0
  60. package/songs/lyr/Z2510136.lyr +54 -0
  61. package/songs/lyr/Z2510137.lyr +66 -0
  62. package/songs/lyr/Z2510138.lyr +62 -0
  63. package/songs/lyr/Z2510139.lyr +60 -0
  64. package/songs/lyr/Z2510140.lyr +41 -0
  65. package/songs/lyr/Z2510141.lyr +48 -0
  66. package/songs/lyr/song.lyr +37 -0
  67. package/songs/midi/BPL3457.MID +0 -0
  68. package/songs/midi/Z2510006.mid +0 -0
  69. package/songs/midi/Z2510008.mid +0 -0
  70. package/songs/midi/Z2510136.mid +0 -0
  71. package/songs/midi/Z2510137.mid +0 -0
  72. package/songs/midi/Z2510138.mid +0 -0
  73. package/songs/midi/Z2510139.mid +0 -0
  74. package/songs/midi/Z2510140.mid +0 -0
  75. package/songs/midi/Z2510141.mid +0 -0
package/BROWSER_API.md ADDED
@@ -0,0 +1,318 @@
1
+ # Browser API Documentation
2
+
3
+ This document describes how to use file-coder in browser and Next.js client-side environments.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install file-coder
9
+ ```
10
+
11
+ ## Usage in Next.js Client Components
12
+
13
+ ### Basic Import
14
+
15
+ ```tsx
16
+ 'use client';
17
+
18
+ import {
19
+ convertEmkFileToKar,
20
+ convertEmkToKarBrowser,
21
+ type BrowserEmkToKarResult
22
+ } from 'file-coder/client';
23
+ ```
24
+
25
+ ### Example 1: Single File Conversion with Auto-Download
26
+
27
+ ```tsx
28
+ 'use client';
29
+
30
+ import { convertEmkFileToKar } from 'file-coder/client';
31
+
32
+ function MyComponent() {
33
+ const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
34
+ const file = e.target.files?.[0];
35
+ if (!file) return;
36
+
37
+ try {
38
+ // Convert and auto-download
39
+ const result = await convertEmkFileToKar(file, {
40
+ autoDownload: true
41
+ });
42
+
43
+ console.log('Converted:', result.metadata.title);
44
+ console.log('Artist:', result.metadata.artist);
45
+ } catch (error) {
46
+ console.error('Conversion failed:', error);
47
+ }
48
+ };
49
+
50
+ return <input type="file" accept=".emk" onChange={handleFileUpload} />;
51
+ }
52
+ ```
53
+
54
+ ### Example 2: Buffer-Based Conversion
55
+
56
+ ```tsx
57
+ 'use client';
58
+
59
+ import { convertEmkToKarBrowser, fileToBuffer } from 'file-coder/client';
60
+
61
+ async function convertFromBuffer(emkFile: File) {
62
+ // Convert File to Buffer
63
+ const emkBuffer = await fileToBuffer(emkFile);
64
+
65
+ // Convert EMK to KAR
66
+ const result = convertEmkToKarBrowser({
67
+ emkBuffer,
68
+ outputFileName: 'output.kar',
69
+ autoDownload: false // We'll handle the buffer ourselves
70
+ });
71
+
72
+ // Access the KAR buffer
73
+ console.log('KAR buffer size:', result.karBuffer.length);
74
+ console.log('Title:', result.metadata.title);
75
+
76
+ // Manually download if needed
77
+ if (result.success) {
78
+ const { downloadBuffer } = await import('file-coder/client');
79
+ downloadBuffer(result.karBuffer, result.fileName);
80
+ }
81
+
82
+ return result;
83
+ }
84
+ ```
85
+
86
+ ### Example 3: Batch Conversion
87
+
88
+ ```tsx
89
+ 'use client';
90
+
91
+ import { convertEmkFilesBatch } from 'file-coder/client';
92
+
93
+ function BatchConverter() {
94
+ const handleBatchUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
95
+ const files = Array.from(e.target.files || []);
96
+ if (files.length === 0) return;
97
+
98
+ try {
99
+ // Convert multiple files
100
+ const results = await convertEmkFilesBatch(files, {
101
+ autoDownload: true
102
+ });
103
+
104
+ const successCount = results.filter(r => r.success).length;
105
+ console.log(`Converted ${successCount}/${files.length} files`);
106
+
107
+ // Check results
108
+ results.forEach(result => {
109
+ if (result.success) {
110
+ console.log(`✓ ${result.fileName}: ${result.metadata.title}`);
111
+ } else {
112
+ console.error(`✗ ${result.fileName}:`, result.warnings);
113
+ }
114
+ });
115
+ } catch (error) {
116
+ console.error('Batch conversion failed:', error);
117
+ }
118
+ };
119
+
120
+ return (
121
+ <input
122
+ type="file"
123
+ accept=".emk"
124
+ multiple
125
+ onChange={handleBatchUpload}
126
+ />
127
+ );
128
+ }
129
+ ```
130
+
131
+ ## API Reference
132
+
133
+ ### Main Functions
134
+
135
+ #### `convertEmkFileToKar(emkFile: File, options?: { autoDownload?: boolean })`
136
+
137
+ Converts a File object (from file input) to KAR format.
138
+
139
+ **Parameters:**
140
+ - `emkFile`: File object from file input
141
+ - `options.autoDownload`: If true, automatically downloads the KAR file (default: true)
142
+
143
+ **Returns:** `Promise<BrowserEmkToKarResult>`
144
+
145
+ #### `convertEmkToKarBrowser(options: BrowserEmkToKarOptions)`
146
+
147
+ Converts an EMK buffer to KAR buffer.
148
+
149
+ **Parameters:**
150
+ ```typescript
151
+ interface BrowserEmkToKarOptions {
152
+ emkBuffer: Buffer;
153
+ outputFileName?: string;
154
+ autoDownload?: boolean;
155
+ }
156
+ ```
157
+
158
+ **Returns:** `BrowserEmkToKarResult`
159
+
160
+ ```typescript
161
+ interface BrowserEmkToKarResult {
162
+ success: boolean;
163
+ karBuffer: Buffer;
164
+ fileName: string;
165
+ intermediateBuffers?: {
166
+ midi: Buffer;
167
+ lyric: Buffer;
168
+ cursor: Buffer;
169
+ };
170
+ metadata: {
171
+ title: string;
172
+ artist: string;
173
+ code?: string;
174
+ };
175
+ warnings: string[];
176
+ }
177
+ ```
178
+
179
+ #### `convertEmkFilesBatch(emkFiles: File[], options?: { autoDownload?: boolean })`
180
+
181
+ Converts multiple EMK files at once.
182
+
183
+ **Parameters:**
184
+ - `emkFiles`: Array of File objects
185
+ - `options.autoDownload`: If true, automatically downloads all KAR files (default: false)
186
+
187
+ **Returns:** `Promise<BrowserEmkToKarResult[]>`
188
+
189
+ ### Helper Functions
190
+
191
+ #### `fileToBuffer(file: File): Promise<Buffer>`
192
+
193
+ Converts a File object to a Buffer for use with buffer-based APIs.
194
+
195
+ #### `downloadBuffer(buffer: Buffer, fileName: string, mimeType?: string): void`
196
+
197
+ Triggers a browser download of a buffer.
198
+
199
+ **Parameters:**
200
+ - `buffer`: The data to download
201
+ - `fileName`: Name for the downloaded file
202
+ - `mimeType`: MIME type (default: 'audio/midi')
203
+
204
+ ### NCN to KAR (Browser)
205
+
206
+ #### `convertNcnToKarBrowser(options: BrowserConversionOptions)`
207
+
208
+ Converts MIDI, Lyric, and Cursor buffers to a KAR buffer.
209
+
210
+ **Parameters:**
211
+ ```typescript
212
+ interface BrowserConversionOptions {
213
+ midiBuffer: Buffer;
214
+ lyricBuffer: Buffer;
215
+ cursorBuffer: Buffer;
216
+ outputFileName?: string;
217
+ }
218
+ ```
219
+
220
+ **Returns:** `BrowserConversionResult`
221
+
222
+ ### KAR Reader (Browser)
223
+
224
+ #### `readKarBuffer(buffer: Buffer): KarFileInfo`
225
+
226
+ Reads and parses a KAR file buffer.
227
+
228
+ #### `validateKarBuffer(buffer: Buffer)`
229
+
230
+ Validates a KAR buffer structure.
231
+
232
+ #### `extractLyricsFromKarBuffer(buffer: Buffer): string[]`
233
+
234
+ Extracts lyrics from a KAR buffer.
235
+
236
+ #### `readKarFile(file: File): Promise<KarFileInfo>`
237
+
238
+ Reads a File object as a KAR file.
239
+
240
+ ## Complete Example Component
241
+
242
+ See [examples/NextJSComponent.tsx](./examples/NextJSComponent.tsx) for a full-featured Next.js component with:
243
+ - Single file upload and conversion
244
+ - Batch file processing
245
+ - Error handling
246
+ - Loading states
247
+ - Success/error messages
248
+ - Auto-download functionality
249
+
250
+ ## Browser Compatibility
251
+
252
+ - ✅ Chrome/Edge (Chromium)
253
+ - ✅ Firefox
254
+ - ✅ Safari
255
+ - ✅ Next.js 13+ (App Router)
256
+ - ✅ React 18+
257
+
258
+ ## Features
259
+
260
+ - 🎵 Client-side EMK decoding
261
+ - 🎤 Thai lyrics support (TIS-620 encoding)
262
+ - 📦 No server required - all processing in browser
263
+ - ⚡ Fast conversion
264
+ - 💾 Auto-download support
265
+ - 🔄 Batch processing
266
+ - 📊 Metadata extraction
267
+ - ✅ KAR file validation
268
+
269
+ ## Notes
270
+
271
+ - All processing happens in the browser - no files are uploaded to servers
272
+ - Large files may take a few seconds to process
273
+ - The library uses Pako for decompression (browser-compatible alternative to zlib)
274
+ - Supports TIS-620 encoding for proper Thai character handling
275
+
276
+ ## TypeScript Support
277
+
278
+ Full TypeScript support with type definitions included:
279
+
280
+ ```typescript
281
+ import type {
282
+ BrowserEmkToKarOptions,
283
+ BrowserEmkToKarResult,
284
+ BrowserConversionOptions,
285
+ BrowserConversionResult,
286
+ KarFileInfo,
287
+ KarTrack
288
+ } from 'file-coder/client';
289
+ ```
290
+
291
+ ## Performance Tips
292
+
293
+ 1. **Batch Processing**: Use `convertEmkFilesBatch` for multiple files instead of calling `convertEmkFileToKar` in a loop
294
+ 2. **Auto-Download**: Set `autoDownload: false` if you want to process the buffer before downloading
295
+ 3. **Web Workers**: For heavy processing, consider running conversions in a Web Worker (not included in library)
296
+
297
+ ## Error Handling
298
+
299
+ ```tsx
300
+ try {
301
+ const result = await convertEmkFileToKar(file);
302
+
303
+ if (result.success) {
304
+ console.log('Success:', result.metadata.title);
305
+
306
+ if (result.warnings.length > 0) {
307
+ console.warn('Warnings:', result.warnings);
308
+ }
309
+ }
310
+ } catch (error) {
311
+ console.error('Conversion failed:', error.message);
312
+ }
313
+ ```
314
+
315
+ ## License
316
+
317
+ MIT
318
+
package/README.md ADDED
@@ -0,0 +1,216 @@
1
+ # @karaplay/file-coder
2
+
3
+ A comprehensive library for encoding/decoding karaoke files (.emk, .kar, MIDI) with Next.js support.
4
+
5
+ ## Features
6
+
7
+ - 🎵 Convert NCN format (.mid + .lyr + .cur) to .kar (MIDI karaoke)
8
+ - 🔓 Decode .emk (Extreme Karaoke) files
9
+ - 📖 Read and validate .kar files
10
+ - ⚡ **NEW**: Complete EMK → KAR workflow (one-step conversion)
11
+ - 🌐 Full TypeScript support
12
+ - ⚛️ Next.js compatible (both client and server side)
13
+ - 🌐 **NEW**: Browser-compatible API for client-side processing
14
+ - 🇹🇭 Thai language support (TIS-620 encoding)
15
+ - ✅ 101+ tests - 100% pass rate (including integration tests with real files)
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @karaplay/file-coder
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ### Browser/Client-Side (Next.js) 🌐 NEW
26
+
27
+ For client-side processing in Next.js or browser environments:
28
+
29
+ ```tsx
30
+ 'use client';
31
+
32
+ import { convertEmkFileToKar } from '@karaplay/file-coder/client';
33
+
34
+ function MyComponent() {
35
+ const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
36
+ const file = e.target.files?.[0];
37
+ if (!file) return;
38
+
39
+ const result = await convertEmkFileToKar(file, { autoDownload: true });
40
+ console.log('Converted:', result.metadata.title);
41
+ };
42
+
43
+ return <input type="file" accept=".emk" onChange={handleFileUpload} />;
44
+ }
45
+ ```
46
+
47
+ **📚 [Full Browser API Documentation →](./BROWSER_API.md)**
48
+
49
+ ### Server-Side (Node.js)
50
+
51
+ ## Usage
52
+
53
+ ### EMK to KAR Workflow (Complete Pipeline) ⭐ NEW
54
+
55
+ Convert EMK files directly to KAR in one step:
56
+
57
+ ```typescript
58
+ import { convertEmkToKar } from '@karaplay/file-coder';
59
+
60
+ const result = convertEmkToKar({
61
+ inputEmk: 'songs/song.emk',
62
+ outputKar: 'output/song.kar'
63
+ });
64
+
65
+ console.log(`Title: ${result.metadata.title}`);
66
+ console.log(`Artist: ${result.metadata.artist}`);
67
+ console.log(`Success: ${result.success}`);
68
+ // Thai text is readable! ✅
69
+ ```
70
+
71
+ **Batch conversion**:
72
+ ```typescript
73
+ import { convertEmkToKarBatch } from '@karaplay/file-coder';
74
+
75
+ const results = convertEmkToKarBatch(
76
+ ['song1.emk', 'song2.emk', 'song3.emk'],
77
+ 'output/kar'
78
+ );
79
+ ```
80
+
81
+ See [EMK_TO_KAR_WORKFLOW.md](./EMK_TO_KAR_WORKFLOW.md) for complete documentation.
82
+
83
+ ### NCN to KAR Conversion
84
+
85
+ ```typescript
86
+ import { convertNcnToKar } from 'file-coder';
87
+
88
+ const result = convertNcnToKar({
89
+ inputMidi: 'path/to/song.mid',
90
+ inputLyr: 'path/to/song.lyr',
91
+ inputCur: 'path/to/song.cur',
92
+ outputKar: 'path/to/output.kar',
93
+ appendTitles: true
94
+ });
95
+
96
+ console.log(`Converted: ${result.metadata.title} by ${result.metadata.artist}`);
97
+ ```
98
+
99
+ ### EMK Decoding (Server-side)
100
+
101
+ ```typescript
102
+ import { decodeEmkServer, parseSongInfoServer } from 'file-coder';
103
+ import * as fs from 'fs';
104
+
105
+ const fileBuffer = fs.readFileSync('path/to/song.emk');
106
+ const decoded = decodeEmkServer(fileBuffer);
107
+
108
+ // Access decoded parts
109
+ const midiData = decoded.midi;
110
+ const lyricData = decoded.lyric;
111
+ const cursorData = decoded.cursor;
112
+
113
+ // Parse song information
114
+ const songInfo = parseSongInfoServer(decoded.songInfo);
115
+ console.log(`Title: ${songInfo.TITLE}`);
116
+ console.log(`Artist: ${songInfo.ARTIST}`);
117
+ ```
118
+
119
+ ### EMK Decoding (Client-side/Next.js)
120
+
121
+ ```typescript
122
+ 'use client';
123
+
124
+ import { decodeEmkClient, parseSongInfoClient } from 'file-coder';
125
+
126
+ function MyComponent() {
127
+ const handleFileUpload = async (file: File) => {
128
+ const arrayBuffer = await file.arrayBuffer();
129
+ const buffer = Buffer.from(arrayBuffer);
130
+
131
+ const decoded = decodeEmkClient(buffer);
132
+ const songInfo = parseSongInfoClient(decoded.songInfo);
133
+
134
+ console.log(`Title: ${songInfo.TITLE}`);
135
+ };
136
+
137
+ return <input type="file" onChange={(e) => handleFileUpload(e.target.files[0])} />;
138
+ }
139
+ ```
140
+
141
+ ## API Reference
142
+
143
+ ### NCN to KAR Functions
144
+
145
+ - `convertNcnToKar(options)` - Main conversion function
146
+ - `parseLyricFile(filePath)` - Parse .lyr file for metadata
147
+ - `buildKaraokeTrack(metadata, cursorBuffer, ticksPerBeat)` - Build karaoke track with timing
148
+ - `buildMetadataTracks(metadata)` - Build metadata tracks
149
+ - `CursorReader` - Class for reading cursor timing data
150
+
151
+ ### EMK to KAR Workflow Functions ⭐ NEW
152
+
153
+ - `convertEmkToKar(options)` - Complete pipeline: EMK → KAR (one step!)
154
+ - `convertEmkToKarBatch(emkFiles, outputDir, options?)` - Batch convert multiple files
155
+ - `validateThaiLyricReadability(lyricBuffer)` - Validate Thai text readability
156
+
157
+ ### KAR File Reader Functions
158
+
159
+ - `readKarFile(filePath)` - Read and parse .kar file
160
+ - `validateKarFile(filePath)` - Validate .kar file structure
161
+ - `extractLyricsFromKar(filePath)` - Extract lyrics from .kar file
162
+
163
+ ### EMK Decoder Functions
164
+
165
+ #### Server-side
166
+ - `decodeEmkServer(fileBuffer)` - Decode .emk file (Node.js)
167
+ - `parseSongInfoServer(songInfoBuffer)` - Parse song metadata
168
+ - `xorDecryptServer(data)` - XOR decryption utility
169
+ - `looksLikeTextServer(buffer)` - Text detection utility
170
+
171
+ #### Client-side
172
+ - `decodeEmkClient(fileBuffer)` - Decode .emk file (Browser/Next.js)
173
+ - `parseSongInfoClient(songInfoBuffer)` - Parse song metadata
174
+ - `xorDecryptClient(data)` - XOR decryption utility
175
+ - `looksLikeTextClient(buffer)` - Text detection utility
176
+
177
+ ## Development
178
+
179
+ ### Install Dependencies
180
+
181
+ ```bash
182
+ npm install
183
+ ```
184
+
185
+ ### Build
186
+
187
+ ```bash
188
+ npm run build
189
+ ```
190
+
191
+ ### Run Tests
192
+
193
+ ```bash
194
+ npm test
195
+ ```
196
+
197
+ ### Test Coverage
198
+
199
+ ```bash
200
+ npm run test:coverage
201
+ ```
202
+
203
+ ## Requirements
204
+
205
+ - Node.js >= 16
206
+ - TypeScript >= 5.0
207
+ - Next.js >= 13.0 (optional, for client-side features)
208
+
209
+ ## License
210
+
211
+ MIT
212
+
213
+ ## Contributing
214
+
215
+ Contributions are welcome! Please feel free to submit a Pull Request.
216
+