metadata-connect 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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +196 -0
  3. package/dist/extract.d.ts +36 -0
  4. package/dist/extract.d.ts.map +1 -0
  5. package/dist/extract.js +77 -0
  6. package/dist/extract.js.map +1 -0
  7. package/dist/index.d.ts +6 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +8 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/parsers/aiff.d.ts +9 -0
  12. package/dist/parsers/aiff.d.ts.map +1 -0
  13. package/dist/parsers/aiff.js +42 -0
  14. package/dist/parsers/aiff.js.map +1 -0
  15. package/dist/parsers/flac.d.ts +6 -0
  16. package/dist/parsers/flac.d.ts.map +1 -0
  17. package/dist/parsers/flac.js +172 -0
  18. package/dist/parsers/flac.js.map +1 -0
  19. package/dist/parsers/id3.d.ts +6 -0
  20. package/dist/parsers/id3.d.ts.map +1 -0
  21. package/dist/parsers/id3.js +329 -0
  22. package/dist/parsers/id3.js.map +1 -0
  23. package/dist/parsers/index.d.ts +6 -0
  24. package/dist/parsers/index.d.ts.map +1 -0
  25. package/dist/parsers/index.js +6 -0
  26. package/dist/parsers/index.js.map +1 -0
  27. package/dist/parsers/mp4.d.ts +6 -0
  28. package/dist/parsers/mp4.d.ts.map +1 -0
  29. package/dist/parsers/mp4.js +295 -0
  30. package/dist/parsers/mp4.js.map +1 -0
  31. package/dist/parsers/utils.d.ts +40 -0
  32. package/dist/parsers/utils.d.ts.map +1 -0
  33. package/dist/parsers/utils.js +120 -0
  34. package/dist/parsers/utils.js.map +1 -0
  35. package/dist/reader.d.ts +6 -0
  36. package/dist/reader.d.ts.map +1 -0
  37. package/dist/reader.js +14 -0
  38. package/dist/reader.js.map +1 -0
  39. package/dist/types.d.ts +70 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +28 -0
  42. package/dist/types.js.map +1 -0
  43. package/package.json +75 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Chris Le
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,196 @@
1
+ # metadata-connect
2
+
3
+ Extract audio metadata from MP3, M4A, FLAC, and AIFF files with support for partial file reads - perfect for extracting metadata from remote files over the network without downloading entire files.
4
+
5
+ [![npm version](https://badge.fury.io/js/metadata-connect.svg)](https://www.npmjs.com/package/metadata-connect)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Features
9
+
10
+ - **Partial file reading** - Extract metadata by reading only file headers (10-200KB instead of entire files)
11
+ - **Multiple format support** - MP3 (ID3v2), M4A/MP4/AAC, FLAC, AIFF
12
+ - **Complete metadata extraction** - Title, artist, album, genre, year, BPM, key, and artwork
13
+ - **FileReader abstraction** - Works with any data source (local files, NFS, HTTP range requests, custom protocols)
14
+ - **Zero runtime dependencies** - Pure TypeScript implementation
15
+ - **TypeScript first** - Full type definitions included
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install metadata-connect
21
+ ```
22
+
23
+ ## Quick Start
24
+
25
+ ### Extract from a local file
26
+
27
+ ```typescript
28
+ import { extractMetadata, createBufferReader } from 'metadata-connect';
29
+ import { readFile } from 'fs/promises';
30
+
31
+ const buffer = await readFile('song.mp3');
32
+ const reader = createBufferReader(buffer, 'mp3');
33
+ const metadata = await extractMetadata(reader);
34
+
35
+ if (metadata) {
36
+ console.log(metadata.title); // "Song Title"
37
+ console.log(metadata.artist); // "Artist Name"
38
+ console.log(metadata.bpm); // 128
39
+ console.log(metadata.key); // "Am"
40
+
41
+ if (metadata.artwork) {
42
+ // metadata.artwork is a Buffer
43
+ // metadata.artworkMimeType is 'image/jpeg' | 'image/png' | 'image/gif'
44
+ }
45
+ }
46
+ ```
47
+
48
+ ### Extract from a remote file (partial read)
49
+
50
+ The real power of metadata-connect is extracting metadata from remote files without downloading them entirely:
51
+
52
+ ```typescript
53
+ import { extractMetadata } from 'metadata-connect';
54
+ import type { FileReader } from 'metadata-connect';
55
+
56
+ // Create a FileReader that fetches only the requested bytes
57
+ const reader: FileReader = {
58
+ size: fileSize, // Total file size (get from HEAD request or file stat)
59
+ extension: 'mp3',
60
+ async read(offset: number, length: number): Promise<Buffer> {
61
+ // Use HTTP Range requests, NFS, or any protocol
62
+ const response = await fetch(url, {
63
+ headers: { Range: `bytes=${offset}-${offset + length - 1}` }
64
+ });
65
+ return Buffer.from(await response.arrayBuffer());
66
+ }
67
+ };
68
+
69
+ const metadata = await extractMetadata(reader);
70
+ ```
71
+
72
+ ## API Reference
73
+
74
+ ### `extractMetadata(reader: FileReader): Promise<ExtractedMetadata | null>`
75
+
76
+ Main extraction function. Returns metadata or null if the format is not supported or parsing fails.
77
+
78
+ ### `createBufferReader(buffer: Buffer, extension: string): FileReader`
79
+
80
+ Create a FileReader from a Buffer for in-memory extraction.
81
+
82
+ ### `isExtensionSupported(extension: string): boolean`
83
+
84
+ Check if a file extension is supported.
85
+
86
+ ### `getSupportedExtensions(): string[]`
87
+
88
+ Get list of supported extensions: `['mp3', 'm4a', 'mp4', 'aac', 'flac', 'aiff', 'aif']`
89
+
90
+ ### `getParserForExtension(extension: string): MetadataParser | null`
91
+
92
+ Get the parser function for a specific extension.
93
+
94
+ ## Types
95
+
96
+ ### `FileReader`
97
+
98
+ ```typescript
99
+ interface FileReader {
100
+ /** Total file size in bytes */
101
+ readonly size: number;
102
+ /** File extension (without dot), e.g., 'mp3', 'flac' */
103
+ readonly extension: string;
104
+ /** Read bytes from the file at a given offset */
105
+ read(offset: number, length: number): Promise<Buffer>;
106
+ }
107
+ ```
108
+
109
+ ### `ExtractedMetadata`
110
+
111
+ ```typescript
112
+ interface ExtractedMetadata {
113
+ title?: string;
114
+ artist?: string;
115
+ album?: string;
116
+ genre?: string;
117
+ year?: number;
118
+ bpm?: number;
119
+ key?: string;
120
+ artwork?: Buffer;
121
+ artworkMimeType?: 'image/jpeg' | 'image/png' | 'image/gif';
122
+ }
123
+ ```
124
+
125
+ ## Format Support
126
+
127
+ | Format | Extension | Metadata Source |
128
+ |--------|-----------|-----------------|
129
+ | MP3 | `.mp3` | ID3v2.2, ID3v2.3, ID3v2.4 tags |
130
+ | M4A/MP4 | `.m4a`, `.mp4`, `.aac` | iTunes metadata atoms |
131
+ | FLAC | `.flac` | Vorbis comments + PICTURE blocks |
132
+ | AIFF | `.aiff`, `.aif` | ID3 chunk |
133
+
134
+ ### Extracted Fields
135
+
136
+ | Field | MP3 | M4A | FLAC | AIFF |
137
+ |-------|-----|-----|------|------|
138
+ | Title | TIT2 | ©nam | TITLE | ID3 |
139
+ | Artist | TPE1 | ©ART | ARTIST | ID3 |
140
+ | Album | TALB | ©alb | ALBUM | ID3 |
141
+ | Genre | TCON | ©gen/gnre | GENRE | ID3 |
142
+ | Year | TYER/TDRC | ©day | DATE | ID3 |
143
+ | BPM | TBPM | tmpo | BPM/TEMPO | ID3 |
144
+ | Key | TKEY | - | KEY/INITIALKEY | ID3 |
145
+ | Artwork | APIC | covr | PICTURE | ID3 |
146
+
147
+ ## Network Efficiency
148
+
149
+ The library is designed to minimize network transfer when reading from remote sources:
150
+
151
+ | Format | Typical Bytes Read |
152
+ |--------|-------------------|
153
+ | MP3 | 10-50 KB (ID3v2 tag at file start) |
154
+ | FLAC | 10 KB (metadata blocks at file start) |
155
+ | M4A/MP4 | 50-200 KB (atom tree traversal) |
156
+ | AIFF | 10-50 KB (ID3 chunk location varies) |
157
+
158
+ ## Use Cases
159
+
160
+ - **DJ Software** - Extract track info from CDJs/controllers over network protocols
161
+ - **Media Servers** - Index large music libraries without reading entire files
162
+ - **Streaming Services** - Quick metadata lookup for remote storage
163
+ - **Browser Applications** - Extract metadata using fetch with Range headers
164
+
165
+ ## Advanced Usage
166
+
167
+ ### Using Individual Parsers
168
+
169
+ ```typescript
170
+ import { extractFromMp3, extractFromMp4, extractFromFlac } from 'metadata-connect';
171
+
172
+ // Use specific parser directly
173
+ const metadata = await extractFromMp3(reader);
174
+ ```
175
+
176
+ ### Detecting Image Types
177
+
178
+ ```typescript
179
+ import { detectImageType } from 'metadata-connect';
180
+
181
+ const mimeType = detectImageType(imageBuffer);
182
+ // Returns 'image/jpeg' | 'image/png' | 'image/gif' | null
183
+ ```
184
+
185
+ ## Contributing
186
+
187
+ Contributions are welcome! Please feel free to submit a Pull Request.
188
+
189
+ ## License
190
+
191
+ MIT License - see [LICENSE](LICENSE) for details.
192
+
193
+ ## Related Projects
194
+
195
+ - [music-metadata](https://github.com/borewit/music-metadata) - Full-featured audio metadata library (requires full file access)
196
+ - [node-id3](https://github.com/Zazama/node-id3) - ID3 tag reader/writer for Node.js
@@ -0,0 +1,36 @@
1
+ import type { ExtractedMetadata, FileReader, MetadataParser } from './types.js';
2
+ /**
3
+ * Get the appropriate parser for a file extension
4
+ */
5
+ export declare function getParserForExtension(extension: string): MetadataParser | null;
6
+ /**
7
+ * Get list of supported file extensions
8
+ */
9
+ export declare function getSupportedExtensions(): string[];
10
+ /**
11
+ * Check if a file extension is supported
12
+ */
13
+ export declare function isExtensionSupported(extension: string): boolean;
14
+ /**
15
+ * Extract metadata from an audio file using the appropriate parser
16
+ *
17
+ * @param reader - FileReader interface for reading file data
18
+ * @returns Extracted metadata, or null if extraction fails or format is unsupported
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * // Create a reader from your transport layer
23
+ * const reader = createFileReader(device, path, fileSize);
24
+ *
25
+ * // Extract metadata
26
+ * const metadata = await extractMetadata(reader);
27
+ * if (metadata) {
28
+ * console.log(metadata.title, metadata.artist);
29
+ * if (metadata.artwork) {
30
+ * // Use artwork buffer
31
+ * }
32
+ * }
33
+ * ```
34
+ */
35
+ export declare function extractMetadata(reader: FileReader): Promise<ExtractedMetadata | null>;
36
+ //# sourceMappingURL=extract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AA6BhF;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAG9E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,MAAM,EAAE,CAEjD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAa3F"}
@@ -0,0 +1,77 @@
1
+ import { extractFromMp3 } from './parsers/id3.js';
2
+ import { extractFromMp4 } from './parsers/mp4.js';
3
+ import { extractFromFlac } from './parsers/flac.js';
4
+ import { extractFromAiff } from './parsers/aiff.js';
5
+ /**
6
+ * Map of file extensions to their metadata parsers
7
+ */
8
+ const PARSER_MAP = {
9
+ // MP3
10
+ mp3: extractFromMp3,
11
+ // MP4/M4A/AAC
12
+ m4a: extractFromMp4,
13
+ mp4: extractFromMp4,
14
+ m4p: extractFromMp4,
15
+ m4b: extractFromMp4,
16
+ aac: extractFromMp4,
17
+ // FLAC
18
+ flac: extractFromFlac,
19
+ // AIFF
20
+ aiff: extractFromAiff,
21
+ aif: extractFromAiff,
22
+ aifc: extractFromAiff,
23
+ };
24
+ /**
25
+ * Get the appropriate parser for a file extension
26
+ */
27
+ export function getParserForExtension(extension) {
28
+ const normalizedExt = extension.toLowerCase().replace(/^\./, '');
29
+ return PARSER_MAP[normalizedExt] ?? null;
30
+ }
31
+ /**
32
+ * Get list of supported file extensions
33
+ */
34
+ export function getSupportedExtensions() {
35
+ return Object.keys(PARSER_MAP);
36
+ }
37
+ /**
38
+ * Check if a file extension is supported
39
+ */
40
+ export function isExtensionSupported(extension) {
41
+ return getParserForExtension(extension) !== null;
42
+ }
43
+ /**
44
+ * Extract metadata from an audio file using the appropriate parser
45
+ *
46
+ * @param reader - FileReader interface for reading file data
47
+ * @returns Extracted metadata, or null if extraction fails or format is unsupported
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * // Create a reader from your transport layer
52
+ * const reader = createFileReader(device, path, fileSize);
53
+ *
54
+ * // Extract metadata
55
+ * const metadata = await extractMetadata(reader);
56
+ * if (metadata) {
57
+ * console.log(metadata.title, metadata.artist);
58
+ * if (metadata.artwork) {
59
+ * // Use artwork buffer
60
+ * }
61
+ * }
62
+ * ```
63
+ */
64
+ export async function extractMetadata(reader) {
65
+ const parser = getParserForExtension(reader.extension);
66
+ if (!parser) {
67
+ return null;
68
+ }
69
+ try {
70
+ return await parser(reader);
71
+ }
72
+ catch {
73
+ // Return null on any parsing error
74
+ return null;
75
+ }
76
+ }
77
+ //# sourceMappingURL=extract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.js","sourceRoot":"","sources":["../src/extract.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD;;GAEG;AACH,MAAM,UAAU,GAAmC;IACjD,MAAM;IACN,GAAG,EAAE,cAAc;IAEnB,cAAc;IACd,GAAG,EAAE,cAAc;IACnB,GAAG,EAAE,cAAc;IACnB,GAAG,EAAE,cAAc;IACnB,GAAG,EAAE,cAAc;IACnB,GAAG,EAAE,cAAc;IAEnB,OAAO;IACP,IAAI,EAAE,eAAe;IAErB,OAAO;IACP,IAAI,EAAE,eAAe;IACrB,GAAG,EAAE,eAAe;IACpB,IAAI,EAAE,eAAe;CACtB,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAiB;IACrD,MAAM,aAAa,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACjE,OAAO,UAAU,CAAC,aAAa,CAAC,IAAI,IAAI,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB;IACpC,OAAO,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,SAAiB;IACpD,OAAO,qBAAqB,CAAC,SAAS,CAAC,KAAK,IAAI,CAAC;AACnD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAkB;IACtD,MAAM,MAAM,GAAG,qBAAqB,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAEvD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,OAAO,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,mCAAmC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { extractMetadata, getParserForExtension, getSupportedExtensions, isExtensionSupported, } from './extract.js';
2
+ export type { FileReader, ExtractedMetadata, ArtworkMimeType, MetadataParser, } from './types.js';
3
+ export { PictureType } from './types.js';
4
+ export { createBufferReader } from './reader.js';
5
+ export { extractFromMp3, extractFromMp4, extractFromFlac, extractFromAiff, detectImageType, normalizeMimeType, } from './parsers/index.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,cAAc,CAAC;AAGtB,YAAY,EACV,UAAU,EACV,iBAAiB,EACjB,eAAe,EACf,cAAc,GACf,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,OAAO,EACL,cAAc,EACd,cAAc,EACd,eAAe,EACf,eAAe,EACf,eAAe,EACf,iBAAiB,GAClB,MAAM,oBAAoB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ // Main extraction function
2
+ export { extractMetadata, getParserForExtension, getSupportedExtensions, isExtensionSupported, } from './extract.js';
3
+ export { PictureType } from './types.js';
4
+ // Reader utilities
5
+ export { createBufferReader } from './reader.js';
6
+ // Individual parsers (for advanced use cases)
7
+ export { extractFromMp3, extractFromMp4, extractFromFlac, extractFromAiff, detectImageType, normalizeMimeType, } from './parsers/index.js';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,oBAAoB,GACrB,MAAM,cAAc,CAAC;AAStB,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,mBAAmB;AACnB,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEjD,8CAA8C;AAC9C,OAAO,EACL,cAAc,EACd,cAAc,EACd,eAAe,EACf,eAAe,EACf,eAAe,EACf,iBAAiB,GAClB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { ExtractedMetadata, FileReader } from '../types.js';
2
+ /**
3
+ * Extract metadata from an AIFF file
4
+ *
5
+ * AIFF files can contain ID3v2 tags in an 'ID3 ' chunk.
6
+ * We look for this chunk and delegate to the ID3 parser.
7
+ */
8
+ export declare function extractFromAiff(reader: FileReader): Promise<ExtractedMetadata | null>;
9
+ //# sourceMappingURL=aiff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aiff.d.ts","sourceRoot":"","sources":["../../src/parsers/aiff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAIjE;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAuC3F"}
@@ -0,0 +1,42 @@
1
+ import { extractFromMp3 } from './id3.js';
2
+ import { createBufferReader } from '../reader.js';
3
+ /**
4
+ * Extract metadata from an AIFF file
5
+ *
6
+ * AIFF files can contain ID3v2 tags in an 'ID3 ' chunk.
7
+ * We look for this chunk and delegate to the ID3 parser.
8
+ */
9
+ export async function extractFromAiff(reader) {
10
+ // Read AIFF header (12 bytes minimum)
11
+ const header = await reader.read(0, 12);
12
+ if (header.length < 12 || header.toString('ascii', 0, 4) !== 'FORM') {
13
+ return null;
14
+ }
15
+ const formType = header.toString('ascii', 8, 12);
16
+ if (formType !== 'AIFF' && formType !== 'AIFC') {
17
+ return null;
18
+ }
19
+ const formSize = header.readUInt32BE(4);
20
+ const fileEnd = Math.min(8 + formSize, reader.size);
21
+ let offset = 12;
22
+ // Iterate through chunks looking for ID3 tag
23
+ while (offset + 8 < fileEnd) {
24
+ const chunkHeader = await reader.read(offset, 8);
25
+ if (chunkHeader.length < 8)
26
+ break;
27
+ const chunkId = chunkHeader.toString('ascii', 0, 4);
28
+ const chunkSize = chunkHeader.readUInt32BE(4);
29
+ if (chunkSize <= 0 || offset + 8 + chunkSize > fileEnd)
30
+ break;
31
+ // Check for ID3 chunk (can be 'ID3 ' or 'id3 ')
32
+ if (chunkId === 'ID3 ' || chunkId === 'id3 ') {
33
+ const id3Data = await reader.read(offset + 8, chunkSize);
34
+ const id3Reader = createBufferReader(id3Data, 'mp3');
35
+ return extractFromMp3(id3Reader);
36
+ }
37
+ // AIFF chunks are padded to even byte boundaries
38
+ offset += 8 + chunkSize + (chunkSize % 2);
39
+ }
40
+ return null;
41
+ }
42
+ //# sourceMappingURL=aiff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aiff.js","sourceRoot":"","sources":["../../src/parsers/aiff.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAElD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAkB;IACtD,sCAAsC;IACtC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;QACpE,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;IACjD,IAAI,QAAQ,KAAK,MAAM,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QAC/C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACxC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IAEpD,IAAI,MAAM,GAAG,EAAE,CAAC;IAEhB,6CAA6C;IAC7C,OAAO,MAAM,GAAG,CAAC,GAAG,OAAO,EAAE,CAAC;QAC5B,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACjD,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM;QAElC,MAAM,OAAO,GAAG,WAAW,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QACpD,MAAM,SAAS,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAE9C,IAAI,SAAS,IAAI,CAAC,IAAI,MAAM,GAAG,CAAC,GAAG,SAAS,GAAG,OAAO;YAAE,MAAM;QAE9D,gDAAgD;QAChD,IAAI,OAAO,KAAK,MAAM,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;YAC7C,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC;YACzD,MAAM,SAAS,GAAG,kBAAkB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YACrD,OAAO,cAAc,CAAC,SAAS,CAAC,CAAC;QACnC,CAAC;QAED,iDAAiD;QACjD,MAAM,IAAI,CAAC,GAAG,SAAS,GAAG,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;IAC5C,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { ExtractedMetadata, FileReader } from '../types.js';
2
+ /**
3
+ * Extract metadata from a FLAC file
4
+ */
5
+ export declare function extractFromFlac(reader: FileReader): Promise<ExtractedMetadata | null>;
6
+ //# sourceMappingURL=flac.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flac.d.ts","sourceRoot":"","sources":["../../src/parsers/flac.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAmB,MAAM,aAAa,CAAC;AAkJlF;;GAEG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CA4E3F"}
@@ -0,0 +1,172 @@
1
+ import { PictureType } from '../types.js';
2
+ import { normalizeMimeType, parseBpm, parseYear, cleanText } from './utils.js';
3
+ /**
4
+ * Vorbis comment field names (case-insensitive)
5
+ */
6
+ const VORBIS_FIELDS = {
7
+ TITLE: ['TITLE'],
8
+ ARTIST: ['ARTIST'],
9
+ ALBUM: ['ALBUM'],
10
+ GENRE: ['GENRE'],
11
+ DATE: ['DATE', 'YEAR'],
12
+ BPM: ['BPM', 'TEMPO'],
13
+ KEY: ['KEY', 'INITIALKEY'],
14
+ };
15
+ /**
16
+ * Parse a FLAC PICTURE metadata block
17
+ */
18
+ function parsePictureBlock(data) {
19
+ if (data.length < 32)
20
+ return null;
21
+ let offset = 0;
22
+ const pictureType = data.readUInt32BE(offset);
23
+ offset += 4;
24
+ const mimeLength = data.readUInt32BE(offset);
25
+ offset += 4;
26
+ if (offset + mimeLength > data.length)
27
+ return null;
28
+ const mimeType = data.toString('utf8', offset, offset + mimeLength);
29
+ offset += mimeLength;
30
+ const descLength = data.readUInt32BE(offset);
31
+ offset += 4 + descLength;
32
+ if (offset + 16 > data.length)
33
+ return null;
34
+ const width = data.readUInt32BE(offset);
35
+ offset += 4;
36
+ const height = data.readUInt32BE(offset);
37
+ offset += 4 + 8; // Skip depth and colors
38
+ const imageLength = data.readUInt32BE(offset);
39
+ offset += 4;
40
+ if (offset + imageLength > data.length)
41
+ return null;
42
+ const imageData = data.subarray(offset, offset + imageLength);
43
+ if (imageData.length === 0)
44
+ return null;
45
+ return {
46
+ data: imageData,
47
+ mimeType: normalizeMimeType(mimeType),
48
+ width: width > 0 ? width : undefined,
49
+ height: height > 0 ? height : undefined,
50
+ pictureType,
51
+ };
52
+ }
53
+ /**
54
+ * Parse a Vorbis comment block
55
+ * Format: vendor string length (32-bit LE) + vendor string + comment count (32-bit LE) + comments
56
+ * Each comment: length (32-bit LE) + "FIELD=value"
57
+ */
58
+ function parseVorbisCommentBlock(data) {
59
+ const comments = {};
60
+ if (data.length < 8)
61
+ return comments;
62
+ let offset = 0;
63
+ // Skip vendor string
64
+ const vendorLength = data.readUInt32LE(offset);
65
+ offset += 4 + vendorLength;
66
+ if (offset + 4 > data.length)
67
+ return comments;
68
+ // Read comment count
69
+ const commentCount = data.readUInt32LE(offset);
70
+ offset += 4;
71
+ // Read each comment
72
+ for (let i = 0; i < commentCount && offset + 4 <= data.length; i++) {
73
+ const commentLength = data.readUInt32LE(offset);
74
+ offset += 4;
75
+ if (offset + commentLength > data.length)
76
+ break;
77
+ const comment = data.toString('utf8', offset, offset + commentLength);
78
+ offset += commentLength;
79
+ // Split on first '='
80
+ const eqIndex = comment.indexOf('=');
81
+ if (eqIndex > 0) {
82
+ const field = comment.substring(0, eqIndex).toUpperCase();
83
+ const value = comment.substring(eqIndex + 1);
84
+ // Only store first value for each field
85
+ if (!comments[field]) {
86
+ comments[field] = value;
87
+ }
88
+ }
89
+ }
90
+ return comments;
91
+ }
92
+ /**
93
+ * Match a Vorbis comment field name against known field names
94
+ */
95
+ function getFieldValue(comments, fieldNames) {
96
+ for (const name of fieldNames) {
97
+ const value = comments[name];
98
+ if (value)
99
+ return cleanText(value);
100
+ }
101
+ return undefined;
102
+ }
103
+ /**
104
+ * Extract metadata from a FLAC file
105
+ */
106
+ export async function extractFromFlac(reader) {
107
+ // Verify FLAC signature
108
+ const signature = await reader.read(0, 4);
109
+ if (signature.toString('ascii') !== 'fLaC') {
110
+ return null;
111
+ }
112
+ const metadata = {};
113
+ let frontCover = null;
114
+ let anyArtwork = null;
115
+ let offset = 4;
116
+ let isLastBlock = false;
117
+ while (!isLastBlock && offset < reader.size) {
118
+ // Read metadata block header (4 bytes)
119
+ const blockHeader = await reader.read(offset, 4);
120
+ if (blockHeader.length < 4)
121
+ break;
122
+ isLastBlock = (blockHeader[0] & 0x80) !== 0;
123
+ const blockType = blockHeader[0] & 0x7f;
124
+ const blockLength = (blockHeader[1] << 16) | (blockHeader[2] << 8) | blockHeader[3];
125
+ if (blockLength <= 0 || offset + 4 + blockLength > reader.size)
126
+ break;
127
+ // Process metadata blocks
128
+ if (blockType === 4 /* MetadataBlockType.VORBIS_COMMENT */) {
129
+ const commentData = await reader.read(offset + 4, blockLength);
130
+ const comments = parseVorbisCommentBlock(commentData);
131
+ // Extract metadata from Vorbis comments
132
+ metadata.title = metadata.title ?? getFieldValue(comments, VORBIS_FIELDS.TITLE);
133
+ metadata.artist = metadata.artist ?? getFieldValue(comments, VORBIS_FIELDS.ARTIST);
134
+ metadata.album = metadata.album ?? getFieldValue(comments, VORBIS_FIELDS.ALBUM);
135
+ metadata.genre = metadata.genre ?? getFieldValue(comments, VORBIS_FIELDS.GENRE);
136
+ const dateValue = getFieldValue(comments, VORBIS_FIELDS.DATE);
137
+ if (dateValue && !metadata.year) {
138
+ metadata.year = parseYear(dateValue);
139
+ }
140
+ const bpmValue = getFieldValue(comments, VORBIS_FIELDS.BPM);
141
+ if (bpmValue && !metadata.bpm) {
142
+ metadata.bpm = parseBpm(bpmValue);
143
+ }
144
+ metadata.key = metadata.key ?? getFieldValue(comments, VORBIS_FIELDS.KEY);
145
+ }
146
+ else if (blockType === 6 /* MetadataBlockType.PICTURE */) {
147
+ const pictureData = await reader.read(offset + 4, blockLength);
148
+ const artwork = parsePictureBlock(pictureData);
149
+ if (artwork) {
150
+ if (artwork.pictureType === PictureType.FrontCover) {
151
+ frontCover = artwork;
152
+ }
153
+ else if (!anyArtwork) {
154
+ anyArtwork = artwork;
155
+ }
156
+ }
157
+ }
158
+ offset += 4 + blockLength;
159
+ }
160
+ // Use front cover if available, otherwise any artwork
161
+ const artwork = frontCover ?? anyArtwork;
162
+ if (artwork) {
163
+ metadata.artwork = artwork.data;
164
+ metadata.artworkMimeType = artwork.mimeType;
165
+ }
166
+ // Return null if no metadata was found
167
+ if (Object.keys(metadata).length === 0) {
168
+ return null;
169
+ }
170
+ return metadata;
171
+ }
172
+ //# sourceMappingURL=flac.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flac.js","sourceRoot":"","sources":["../../src/parsers/flac.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAe/E;;GAEG;AACH,MAAM,aAAa,GAAG;IACpB,KAAK,EAAE,CAAC,OAAO,CAAC;IAChB,MAAM,EAAE,CAAC,QAAQ,CAAC;IAClB,KAAK,EAAE,CAAC,OAAO,CAAC;IAChB,KAAK,EAAE,CAAC,OAAO,CAAC;IAChB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC;IACrB,GAAG,EAAE,CAAC,KAAK,EAAE,YAAY,CAAC;CAClB,CAAC;AAUX;;GAEG;AACH,SAAS,iBAAiB,CAAC,IAAY;IACrC,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE;QAAE,OAAO,IAAI,CAAC;IAElC,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAgB,CAAC;IAC7D,MAAM,IAAI,CAAC,CAAC;IAEZ,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,IAAI,CAAC,CAAC;IAEZ,IAAI,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEnD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC;IACpE,MAAM,IAAI,UAAU,CAAC;IAErB,MAAM,UAAU,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC7C,MAAM,IAAI,CAAC,GAAG,UAAU,CAAC;IAEzB,IAAI,MAAM,GAAG,EAAE,GAAG,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAE3C,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,IAAI,CAAC,CAAC;IAEZ,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,wBAAwB;IAEzC,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC9C,MAAM,IAAI,CAAC,CAAC;IAEZ,IAAI,MAAM,GAAG,WAAW,GAAG,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAEpD,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC;IAC9D,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAExC,OAAO;QACL,IAAI,EAAE,SAAS;QACf,QAAQ,EAAE,iBAAiB,CAAC,QAAQ,CAAC;QACrC,KAAK,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS;QACpC,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS;QACvC,WAAW;KACZ,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,uBAAuB,CAAC,IAAY;IAC3C,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAE5C,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAErC,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,qBAAqB;IACrB,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC/C,MAAM,IAAI,CAAC,GAAG,YAAY,CAAC;IAE3B,IAAI,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM;QAAE,OAAO,QAAQ,CAAC;IAE9C,qBAAqB;IACrB,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IAC/C,MAAM,IAAI,CAAC,CAAC;IAEZ,oBAAoB;IACpB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,IAAI,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnE,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAChD,MAAM,IAAI,CAAC,CAAC;QAEZ,IAAI,MAAM,GAAG,aAAa,GAAG,IAAI,CAAC,MAAM;YAAE,MAAM;QAEhD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,CAAC,CAAC;QACtE,MAAM,IAAI,aAAa,CAAC;QAExB,qBAAqB;QACrB,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACrC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAChB,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;YAC1D,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC;YAC7C,wCAAwC;YACxC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBACrB,QAAQ,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;YAC1B,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CACpB,QAAgC,EAChC,UAA6B;IAE7B,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,KAAK;YAAE,OAAO,SAAS,CAAC,KAAK,CAAC,CAAC;IACrC,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAkB;IACtD,wBAAwB;IACxB,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC1C,IAAI,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,MAAM,EAAE,CAAC;QAC3C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAsB,EAAE,CAAC;IACvC,IAAI,UAAU,GAAyB,IAAI,CAAC;IAC5C,IAAI,UAAU,GAAyB,IAAI,CAAC;IAE5C,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,OAAO,CAAC,WAAW,IAAI,MAAM,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;QAC5C,uCAAuC;QACvC,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QACjD,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM;QAElC,WAAW,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;QACxC,MAAM,WAAW,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAEpF,IAAI,WAAW,IAAI,CAAC,IAAI,MAAM,GAAG,CAAC,GAAG,WAAW,GAAG,MAAM,CAAC,IAAI;YAAE,MAAM;QAEtE,0BAA0B;QAC1B,IAAI,SAAS,6CAAqC,EAAE,CAAC;YACnD,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,WAAW,CAAC,CAAC;YAC/D,MAAM,QAAQ,GAAG,uBAAuB,CAAC,WAAW,CAAC,CAAC;YAEtD,wCAAwC;YACxC,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,IAAI,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;YAChF,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,IAAI,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;YACnF,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,IAAI,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;YAChF,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,KAAK,IAAI,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC;YAEhF,MAAM,SAAS,GAAG,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,IAAI,CAAC,CAAC;YAC9D,IAAI,SAAS,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;gBAChC,QAAQ,CAAC,IAAI,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;YACvC,CAAC;YAED,MAAM,QAAQ,GAAG,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;YAC5D,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;gBAC9B,QAAQ,CAAC,GAAG,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACpC,CAAC;YAED,QAAQ,CAAC,GAAG,GAAG,QAAQ,CAAC,GAAG,IAAI,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC;QAC5E,CAAC;aAAM,IAAI,SAAS,sCAA8B,EAAE,CAAC;YACnD,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,WAAW,CAAC,CAAC;YAC/D,MAAM,OAAO,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;YAE/C,IAAI,OAAO,EAAE,CAAC;gBACZ,IAAI,OAAO,CAAC,WAAW,KAAK,WAAW,CAAC,UAAU,EAAE,CAAC;oBACnD,UAAU,GAAG,OAAO,CAAC;gBACvB,CAAC;qBAAM,IAAI,CAAC,UAAU,EAAE,CAAC;oBACvB,UAAU,GAAG,OAAO,CAAC;gBACvB,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,IAAI,CAAC,GAAG,WAAW,CAAC;IAC5B,CAAC;IAED,sDAAsD;IACtD,MAAM,OAAO,GAAG,UAAU,IAAI,UAAU,CAAC;IACzC,IAAI,OAAO,EAAE,CAAC;QACZ,QAAQ,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;QAChC,QAAQ,CAAC,eAAe,GAAG,OAAO,CAAC,QAAQ,CAAC;IAC9C,CAAC;IAED,uCAAuC;IACvC,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { ExtractedMetadata, FileReader } from '../types.js';
2
+ /**
3
+ * Extract metadata from an MP3 file with ID3v2 tags
4
+ */
5
+ export declare function extractFromMp3(reader: FileReader): Promise<ExtractedMetadata | null>;
6
+ //# sourceMappingURL=id3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"id3.d.ts","sourceRoot":"","sources":["../../src/parsers/id3.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAmB,MAAM,aAAa,CAAC;AA4IlF;;GAEG;AACH,wBAAsB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAuG1F"}