taglib-wasm 0.3.3 → 0.3.9
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.
- package/CONTRIBUTING.md +293 -0
- package/NOTICE +34 -0
- package/README.md +122 -511
- package/dist/index.d.ts +132 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +137 -0
- package/dist/index.ts +220 -0
- package/dist/src/constants.d.ts +201 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.ts +227 -0
- package/dist/src/errors.d.ts +89 -0
- package/dist/src/errors.d.ts.map +1 -0
- package/dist/src/errors.ts +237 -0
- package/dist/src/file-utils.d.ts +205 -0
- package/dist/src/file-utils.d.ts.map +1 -0
- package/dist/src/file-utils.ts +467 -0
- package/dist/src/file.js +47 -0
- package/dist/src/global.d.ts +10 -0
- package/dist/src/mod.d.ts +9 -0
- package/dist/src/mod.d.ts.map +1 -0
- package/dist/src/mod.ts +19 -0
- package/dist/src/simple.d.ts +347 -0
- package/dist/src/simple.d.ts.map +1 -0
- package/dist/src/simple.ts +659 -0
- package/dist/src/taglib.d.ts +502 -0
- package/dist/src/taglib.d.ts.map +1 -0
- package/dist/src/taglib.ts +959 -0
- package/dist/src/types.d.ts +323 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.ts +538 -0
- package/dist/src/utils/file.d.ts +15 -0
- package/dist/src/utils/file.d.ts.map +1 -0
- package/dist/src/utils/file.ts +82 -0
- package/dist/src/utils/write.d.ts +15 -0
- package/dist/src/utils/write.d.ts.map +1 -0
- package/dist/src/utils/write.ts +61 -0
- package/dist/src/wasm-workers.d.ts +33 -0
- package/dist/src/wasm-workers.d.ts.map +1 -0
- package/dist/src/wasm-workers.ts +176 -0
- package/dist/src/wasm.d.ts +97 -0
- package/dist/src/wasm.d.ts.map +1 -0
- package/dist/src/wasm.ts +133 -0
- package/dist/src/web-utils.d.ts +180 -0
- package/dist/src/web-utils.d.ts.map +1 -0
- package/dist/src/web-utils.ts +347 -0
- package/dist/src/workers.d.ts +219 -0
- package/dist/src/workers.d.ts.map +1 -0
- package/dist/src/workers.ts +465 -0
- package/dist/src/write.js +33 -0
- package/dist/taglib-wrapper.d.ts +5 -0
- package/dist/taglib-wrapper.js +14 -0
- package/dist/taglib.wasm +0 -0
- package/index.ts +100 -7
- package/package.json +40 -16
- package/src/errors.ts +237 -0
- package/src/file-utils.ts +467 -0
- package/src/global.d.ts +10 -0
- package/src/simple.ts +399 -84
- package/src/taglib.ts +522 -28
- package/src/types.ts +1 -1
- package/src/utils/file.ts +82 -0
- package/src/utils/write.ts +61 -0
- package/src/wasm-workers.ts +13 -4
- package/src/wasm.ts +1 -1
- package/src/web-utils.ts +347 -0
- package/src/workers.ts +32 -13
- package/build/taglib.js +0 -2407
- package/build/taglib.wasm +0 -0
|
@@ -0,0 +1,467 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview File I/O utilities for working with cover art in taglib-wasm
|
|
3
|
+
*
|
|
4
|
+
* This module provides file system helpers for saving and loading cover art
|
|
5
|
+
* in Node.js, Deno, and Bun environments.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { exportCoverArt, importCoverArt } from "taglib-wasm/file-utils";
|
|
10
|
+
*
|
|
11
|
+
* // Export cover art from audio file to image file
|
|
12
|
+
* await exportCoverArt("song.mp3", "cover.jpg");
|
|
13
|
+
*
|
|
14
|
+
* // Import cover art from image file to audio file
|
|
15
|
+
* await importCoverArt("song.mp3", "new-cover.png");
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import type { Picture } from "./types.ts";
|
|
20
|
+
import { PictureType } from "./types.ts";
|
|
21
|
+
import {
|
|
22
|
+
readPictures,
|
|
23
|
+
applyPictures,
|
|
24
|
+
setCoverArt,
|
|
25
|
+
getCoverArt,
|
|
26
|
+
replacePictureByType,
|
|
27
|
+
} from "./simple.ts";
|
|
28
|
+
import { readFileData } from "./utils/file.ts";
|
|
29
|
+
import { writeFileData } from "./utils/write.ts";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Export cover art from an audio file to an image file
|
|
33
|
+
*
|
|
34
|
+
* Extracts the primary cover art (front cover if available, otherwise first picture)
|
|
35
|
+
* and saves it to the specified path.
|
|
36
|
+
*
|
|
37
|
+
* @param audioPath - Path to the audio file
|
|
38
|
+
* @param imagePath - Path where the image should be saved
|
|
39
|
+
* @returns Promise that resolves when the image is saved
|
|
40
|
+
* @throws Error if no cover art is found
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* // Export cover art as JPEG
|
|
45
|
+
* await exportCoverArt("album/track01.mp3", "album/cover.jpg");
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export async function exportCoverArt(
|
|
49
|
+
audioPath: string,
|
|
50
|
+
imagePath: string,
|
|
51
|
+
): Promise<void> {
|
|
52
|
+
const coverData = await getCoverArt(audioPath);
|
|
53
|
+
if (!coverData) {
|
|
54
|
+
throw new Error(`No cover art found in: ${audioPath}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
await writeFileData(imagePath, coverData);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Export a specific picture type from an audio file
|
|
62
|
+
*
|
|
63
|
+
* @param audioPath - Path to the audio file
|
|
64
|
+
* @param imagePath - Path where the image should be saved
|
|
65
|
+
* @param type - Picture type to export
|
|
66
|
+
* @returns Promise that resolves when the image is saved
|
|
67
|
+
* @throws Error if no picture of the specified type is found
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* // Export back cover
|
|
72
|
+
* await exportPictureByType(
|
|
73
|
+
* "album/track01.mp3",
|
|
74
|
+
* "album/back-cover.jpg",
|
|
75
|
+
* PictureType.BackCover
|
|
76
|
+
* );
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export async function exportPictureByType(
|
|
80
|
+
audioPath: string,
|
|
81
|
+
imagePath: string,
|
|
82
|
+
type: PictureType,
|
|
83
|
+
): Promise<void> {
|
|
84
|
+
const pictures = await readPictures(audioPath);
|
|
85
|
+
const picture = pictures.find(pic => pic.type === type);
|
|
86
|
+
|
|
87
|
+
if (!picture) {
|
|
88
|
+
throw new Error(`No picture of type ${type} found in: ${audioPath}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
await writeFileData(imagePath, picture.data);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Export all pictures from an audio file
|
|
96
|
+
*
|
|
97
|
+
* Saves each picture with a numbered suffix based on its type and index.
|
|
98
|
+
*
|
|
99
|
+
* @param audioPath - Path to the audio file
|
|
100
|
+
* @param outputDir - Directory where images should be saved
|
|
101
|
+
* @param options - Export options
|
|
102
|
+
* @returns Promise resolving to array of created file paths
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* // Export all pictures to a directory
|
|
107
|
+
* const files = await exportAllPictures("song.mp3", "./artwork/");
|
|
108
|
+
* console.log(`Exported ${files.length} pictures`);
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export async function exportAllPictures(
|
|
112
|
+
audioPath: string,
|
|
113
|
+
outputDir: string,
|
|
114
|
+
options: {
|
|
115
|
+
nameFormat?: (picture: Picture, index: number) => string;
|
|
116
|
+
} = {},
|
|
117
|
+
): Promise<string[]> {
|
|
118
|
+
const pictures = await readPictures(audioPath);
|
|
119
|
+
const exportedPaths: string[] = [];
|
|
120
|
+
|
|
121
|
+
// Ensure directory ends with separator
|
|
122
|
+
const dir = outputDir.endsWith('/') ? outputDir : outputDir + '/';
|
|
123
|
+
|
|
124
|
+
for (let i = 0; i < pictures.length; i++) {
|
|
125
|
+
const picture = pictures[i];
|
|
126
|
+
|
|
127
|
+
// Determine filename
|
|
128
|
+
let filename: string;
|
|
129
|
+
if (options.nameFormat) {
|
|
130
|
+
filename = options.nameFormat(picture, i);
|
|
131
|
+
} else {
|
|
132
|
+
// Default naming: type-index.ext
|
|
133
|
+
const typeNames: Record<number, string> = {
|
|
134
|
+
[PictureType.FrontCover]: 'front-cover',
|
|
135
|
+
[PictureType.BackCover]: 'back-cover',
|
|
136
|
+
[PictureType.LeafletPage]: 'leaflet',
|
|
137
|
+
[PictureType.Media]: 'media',
|
|
138
|
+
[PictureType.LeadArtist]: 'lead-artist',
|
|
139
|
+
[PictureType.Artist]: 'artist',
|
|
140
|
+
[PictureType.Conductor]: 'conductor',
|
|
141
|
+
[PictureType.Band]: 'band',
|
|
142
|
+
[PictureType.Composer]: 'composer',
|
|
143
|
+
[PictureType.Lyricist]: 'lyricist',
|
|
144
|
+
[PictureType.RecordingLocation]: 'recording-location',
|
|
145
|
+
[PictureType.DuringRecording]: 'during-recording',
|
|
146
|
+
[PictureType.DuringPerformance]: 'during-performance',
|
|
147
|
+
[PictureType.MovieScreenCapture]: 'screen-capture',
|
|
148
|
+
[PictureType.ColouredFish]: 'fish',
|
|
149
|
+
[PictureType.Illustration]: 'illustration',
|
|
150
|
+
[PictureType.BandLogo]: 'band-logo',
|
|
151
|
+
[PictureType.PublisherLogo]: 'publisher-logo',
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const typeName = typeNames[picture.type] || 'other';
|
|
155
|
+
const ext = picture.mimeType.split('/')[1] || 'jpg';
|
|
156
|
+
filename = `${typeName}-${i + 1}.${ext}`;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const fullPath = dir + filename;
|
|
160
|
+
await writeFileData(fullPath, picture.data);
|
|
161
|
+
exportedPaths.push(fullPath);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return exportedPaths;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Import cover art from an image file to an audio file
|
|
169
|
+
*
|
|
170
|
+
* Replaces all existing pictures with a single front cover from the image file.
|
|
171
|
+
* The audio file is modified in place.
|
|
172
|
+
*
|
|
173
|
+
* @param audioPath - Path to the audio file to update
|
|
174
|
+
* @param imagePath - Path to the image file to import
|
|
175
|
+
* @param options - Import options
|
|
176
|
+
* @returns Promise that resolves when the audio file is updated
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```typescript
|
|
180
|
+
* // Replace cover art with a new image
|
|
181
|
+
* await importCoverArt("song.mp3", "new-cover.jpg");
|
|
182
|
+
* ```
|
|
183
|
+
*/
|
|
184
|
+
export async function importCoverArt(
|
|
185
|
+
audioPath: string,
|
|
186
|
+
imagePath: string,
|
|
187
|
+
options: {
|
|
188
|
+
mimeType?: string;
|
|
189
|
+
description?: string;
|
|
190
|
+
} = {},
|
|
191
|
+
): Promise<void> {
|
|
192
|
+
const imageData = await readFileData(imagePath);
|
|
193
|
+
|
|
194
|
+
// Detect MIME type from file extension if not provided
|
|
195
|
+
let mimeType = options.mimeType;
|
|
196
|
+
if (!mimeType) {
|
|
197
|
+
const ext = imagePath.split('.').pop()?.toLowerCase();
|
|
198
|
+
const mimeTypes: Record<string, string> = {
|
|
199
|
+
'jpg': 'image/jpeg',
|
|
200
|
+
'jpeg': 'image/jpeg',
|
|
201
|
+
'png': 'image/png',
|
|
202
|
+
'gif': 'image/gif',
|
|
203
|
+
'webp': 'image/webp',
|
|
204
|
+
'bmp': 'image/bmp',
|
|
205
|
+
};
|
|
206
|
+
mimeType = mimeTypes[ext || ''] || 'image/jpeg';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const modifiedBuffer = await setCoverArt(audioPath, imageData, mimeType);
|
|
210
|
+
await writeFileData(audioPath, modifiedBuffer);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Import a picture from file with specific type
|
|
215
|
+
*
|
|
216
|
+
* Adds or replaces a picture of the specified type in the audio file.
|
|
217
|
+
* The audio file is modified in place.
|
|
218
|
+
*
|
|
219
|
+
* @param audioPath - Path to the audio file to update
|
|
220
|
+
* @param imagePath - Path to the image file to import
|
|
221
|
+
* @param type - Picture type to set
|
|
222
|
+
* @param options - Import options
|
|
223
|
+
* @returns Promise that resolves when the audio file is updated
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* // Add a back cover
|
|
228
|
+
* await importPictureWithType(
|
|
229
|
+
* "song.mp3",
|
|
230
|
+
* "back-cover.jpg",
|
|
231
|
+
* PictureType.BackCover,
|
|
232
|
+
* { description: "Album back cover" }
|
|
233
|
+
* );
|
|
234
|
+
* ```
|
|
235
|
+
*/
|
|
236
|
+
export async function importPictureWithType(
|
|
237
|
+
audioPath: string,
|
|
238
|
+
imagePath: string,
|
|
239
|
+
type: PictureType,
|
|
240
|
+
options: {
|
|
241
|
+
mimeType?: string;
|
|
242
|
+
description?: string;
|
|
243
|
+
} = {},
|
|
244
|
+
): Promise<void> {
|
|
245
|
+
const imageData = await readFileData(imagePath);
|
|
246
|
+
|
|
247
|
+
// Detect MIME type from file extension if not provided
|
|
248
|
+
let mimeType = options.mimeType;
|
|
249
|
+
if (!mimeType) {
|
|
250
|
+
const ext = imagePath.split('.').pop()?.toLowerCase();
|
|
251
|
+
const mimeTypes: Record<string, string> = {
|
|
252
|
+
'jpg': 'image/jpeg',
|
|
253
|
+
'jpeg': 'image/jpeg',
|
|
254
|
+
'png': 'image/png',
|
|
255
|
+
'gif': 'image/gif',
|
|
256
|
+
'webp': 'image/webp',
|
|
257
|
+
'bmp': 'image/bmp',
|
|
258
|
+
};
|
|
259
|
+
mimeType = mimeTypes[ext || ''] || 'image/jpeg';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const picture: Picture = {
|
|
263
|
+
mimeType,
|
|
264
|
+
data: imageData,
|
|
265
|
+
type,
|
|
266
|
+
description: options.description,
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
const modifiedBuffer = await replacePictureByType(audioPath, picture);
|
|
270
|
+
await writeFileData(audioPath, modifiedBuffer);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Load a picture from an image file
|
|
275
|
+
*
|
|
276
|
+
* @param imagePath - Path to the image file
|
|
277
|
+
* @param type - Picture type (defaults to FrontCover)
|
|
278
|
+
* @param options - Picture options
|
|
279
|
+
* @returns Picture object ready to be applied to audio files
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* const frontCover = await loadPictureFromFile("cover.jpg");
|
|
284
|
+
* const backCover = await loadPictureFromFile("back.png", PictureType.BackCover);
|
|
285
|
+
*
|
|
286
|
+
* const modifiedBuffer = await applyPictures("song.mp3", [frontCover, backCover]);
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
export async function loadPictureFromFile(
|
|
290
|
+
imagePath: string,
|
|
291
|
+
type: PictureType = PictureType.FrontCover,
|
|
292
|
+
options: {
|
|
293
|
+
mimeType?: string;
|
|
294
|
+
description?: string;
|
|
295
|
+
} = {},
|
|
296
|
+
): Promise<Picture> {
|
|
297
|
+
const data = await readFileData(imagePath);
|
|
298
|
+
|
|
299
|
+
// Detect MIME type from file extension if not provided
|
|
300
|
+
let mimeType = options.mimeType;
|
|
301
|
+
if (!mimeType) {
|
|
302
|
+
const ext = imagePath.split('.').pop()?.toLowerCase();
|
|
303
|
+
const mimeTypes: Record<string, string> = {
|
|
304
|
+
'jpg': 'image/jpeg',
|
|
305
|
+
'jpeg': 'image/jpeg',
|
|
306
|
+
'png': 'image/png',
|
|
307
|
+
'gif': 'image/gif',
|
|
308
|
+
'webp': 'image/webp',
|
|
309
|
+
'bmp': 'image/bmp',
|
|
310
|
+
};
|
|
311
|
+
mimeType = mimeTypes[ext || ''] || 'image/jpeg';
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
mimeType,
|
|
316
|
+
data,
|
|
317
|
+
type,
|
|
318
|
+
description: options.description || imagePath.split('/').pop(),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Save a picture to an image file
|
|
324
|
+
*
|
|
325
|
+
* @param picture - Picture object to save
|
|
326
|
+
* @param imagePath - Path where the image should be saved
|
|
327
|
+
* @returns Promise that resolves when the image is saved
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* ```typescript
|
|
331
|
+
* const pictures = await readPictures("song.mp3");
|
|
332
|
+
* for (const picture of pictures) {
|
|
333
|
+
* await savePictureToFile(picture, `export-${picture.type}.jpg`);
|
|
334
|
+
* }
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
337
|
+
export async function savePictureToFile(
|
|
338
|
+
picture: Picture,
|
|
339
|
+
imagePath: string,
|
|
340
|
+
): Promise<void> {
|
|
341
|
+
await writeFileData(imagePath, picture.data);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Copy cover art from one audio file to another
|
|
346
|
+
*
|
|
347
|
+
* @param sourcePath - Path to source audio file
|
|
348
|
+
* @param targetPath - Path to target audio file (modified in place)
|
|
349
|
+
* @param options - Copy options
|
|
350
|
+
* @returns Promise that resolves when cover art is copied
|
|
351
|
+
* @throws Error if source has no cover art
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* ```typescript
|
|
355
|
+
* // Copy cover art from one file to another
|
|
356
|
+
* await copyCoverArt("album/track01.mp3", "album/track02.mp3");
|
|
357
|
+
*
|
|
358
|
+
* // Copy all pictures
|
|
359
|
+
* await copyCoverArt("source.mp3", "target.mp3", { copyAll: true });
|
|
360
|
+
* ```
|
|
361
|
+
*/
|
|
362
|
+
export async function copyCoverArt(
|
|
363
|
+
sourcePath: string,
|
|
364
|
+
targetPath: string,
|
|
365
|
+
options: {
|
|
366
|
+
copyAll?: boolean;
|
|
367
|
+
} = {},
|
|
368
|
+
): Promise<void> {
|
|
369
|
+
if (options.copyAll) {
|
|
370
|
+
// Copy all pictures
|
|
371
|
+
const pictures = await readPictures(sourcePath);
|
|
372
|
+
if (pictures.length === 0) {
|
|
373
|
+
throw new Error(`No pictures found in: ${sourcePath}`);
|
|
374
|
+
}
|
|
375
|
+
const modifiedBuffer = await applyPictures(targetPath, pictures);
|
|
376
|
+
await writeFileData(targetPath, modifiedBuffer);
|
|
377
|
+
} else {
|
|
378
|
+
// Copy just the primary cover art
|
|
379
|
+
const coverData = await getCoverArt(sourcePath);
|
|
380
|
+
if (!coverData) {
|
|
381
|
+
throw new Error(`No cover art found in: ${sourcePath}`);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Find the MIME type from the source
|
|
385
|
+
const pictures = await readPictures(sourcePath);
|
|
386
|
+
const coverPicture = pictures.find(p => p.type === PictureType.FrontCover) || pictures[0];
|
|
387
|
+
|
|
388
|
+
const modifiedBuffer = await setCoverArt(targetPath, coverData, coverPicture.mimeType);
|
|
389
|
+
await writeFileData(targetPath, modifiedBuffer);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Check if cover art files exist for an audio file
|
|
395
|
+
*
|
|
396
|
+
* Looks for common cover art filenames in the same directory as the audio file.
|
|
397
|
+
*
|
|
398
|
+
* @param audioPath - Path to the audio file
|
|
399
|
+
* @returns Object with found cover art paths
|
|
400
|
+
*
|
|
401
|
+
* @example
|
|
402
|
+
* ```typescript
|
|
403
|
+
* const covers = await findCoverArtFiles("music/album/track01.mp3");
|
|
404
|
+
* if (covers.front) {
|
|
405
|
+
* await importCoverArt("music/album/track01.mp3", covers.front);
|
|
406
|
+
* }
|
|
407
|
+
* ```
|
|
408
|
+
*/
|
|
409
|
+
export async function findCoverArtFiles(
|
|
410
|
+
audioPath: string,
|
|
411
|
+
): Promise<{
|
|
412
|
+
front?: string;
|
|
413
|
+
back?: string;
|
|
414
|
+
folder?: string;
|
|
415
|
+
[key: string]: string | undefined;
|
|
416
|
+
}> {
|
|
417
|
+
const dir = audioPath.substring(0, audioPath.lastIndexOf('/') + 1);
|
|
418
|
+
const commonNames = [
|
|
419
|
+
'cover', 'front', 'folder', 'album', 'artwork',
|
|
420
|
+
'Cover', 'Front', 'Folder', 'Album', 'Artwork',
|
|
421
|
+
];
|
|
422
|
+
const extensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
|
423
|
+
const found: Record<string, string> = {};
|
|
424
|
+
|
|
425
|
+
// Check for common front cover names
|
|
426
|
+
for (const name of commonNames) {
|
|
427
|
+
for (const ext of extensions) {
|
|
428
|
+
const path = `${dir}${name}.${ext}`;
|
|
429
|
+
try {
|
|
430
|
+
// Try to read a single byte to check if file exists
|
|
431
|
+
const data = await readFileData(path);
|
|
432
|
+
if (data.length > 0) {
|
|
433
|
+
if (!found.front && ['cover', 'front', 'Cover', 'Front'].includes(name)) {
|
|
434
|
+
found.front = path;
|
|
435
|
+
} else if (!found.folder && ['folder', 'Folder'].includes(name)) {
|
|
436
|
+
found.folder = path;
|
|
437
|
+
} else {
|
|
438
|
+
found[name.toLowerCase()] = path;
|
|
439
|
+
}
|
|
440
|
+
break; // Found this name, skip other extensions
|
|
441
|
+
}
|
|
442
|
+
} catch {
|
|
443
|
+
// File doesn't exist, continue
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Check for back cover
|
|
449
|
+
const backNames = ['back', 'Back', 'back-cover', 'Back-Cover'];
|
|
450
|
+
for (const name of backNames) {
|
|
451
|
+
for (const ext of extensions) {
|
|
452
|
+
const path = `${dir}${name}.${ext}`;
|
|
453
|
+
try {
|
|
454
|
+
const data = await readFileData(path);
|
|
455
|
+
if (data.length > 0) {
|
|
456
|
+
found.back = path;
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
} catch {
|
|
460
|
+
// File doesn't exist, continue
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (found.back) break;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
return found;
|
|
467
|
+
}
|