taglib-wasm 0.3.13 → 0.3.15

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 (82) hide show
  1. package/README.md +22 -35
  2. package/dist/index.d.ts +10 -10
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +117 -134
  5. package/dist/index.js.map +1 -0
  6. package/dist/src/constants.js +211 -0
  7. package/dist/src/constants.js.map +1 -0
  8. package/dist/src/errors.js +180 -0
  9. package/dist/src/errors.js.map +1 -0
  10. package/dist/src/file-utils.d.ts +2 -2
  11. package/dist/src/file-utils.d.ts.map +1 -1
  12. package/dist/src/file-utils.js +394 -0
  13. package/dist/src/file-utils.js.map +1 -0
  14. package/dist/src/mod.d.ts +4 -4
  15. package/dist/src/mod.d.ts.map +1 -1
  16. package/dist/src/mod.js +7 -0
  17. package/dist/src/mod.js.map +1 -0
  18. package/dist/src/simple.d.ts +4 -4
  19. package/dist/src/simple.d.ts.map +1 -1
  20. package/dist/src/{simple.ts → simple.js} +193 -311
  21. package/dist/src/simple.js.map +1 -0
  22. package/dist/src/taglib.d.ts +3 -3
  23. package/dist/src/taglib.d.ts.map +1 -1
  24. package/dist/src/taglib.js +516 -0
  25. package/dist/src/taglib.js.map +1 -0
  26. package/dist/src/types.d.ts +2 -2
  27. package/dist/src/types.d.ts.map +1 -1
  28. package/dist/src/types.js +239 -0
  29. package/dist/src/types.js.map +1 -0
  30. package/dist/src/utils/file.js +65 -0
  31. package/dist/src/utils/file.js.map +1 -0
  32. package/dist/src/utils/write.js +49 -0
  33. package/dist/src/utils/write.js.map +1 -0
  34. package/dist/src/wasm-workers.d.ts +1 -1
  35. package/dist/src/wasm-workers.d.ts.map +1 -1
  36. package/dist/src/wasm-workers.js +148 -0
  37. package/dist/src/wasm-workers.js.map +1 -0
  38. package/dist/src/wasm.js +6 -0
  39. package/dist/src/wasm.js.map +1 -0
  40. package/dist/src/web-utils.d.ts +2 -2
  41. package/dist/src/web-utils.d.ts.map +1 -1
  42. package/{src/web-utils.ts → dist/src/web-utils.js} +102 -184
  43. package/dist/src/web-utils.js.map +1 -0
  44. package/dist/src/workers.d.ts +2 -2
  45. package/dist/src/workers.d.ts.map +1 -1
  46. package/dist/src/workers.js +389 -0
  47. package/dist/src/workers.js.map +1 -0
  48. package/dist/taglib-wrapper.js +8 -2528
  49. package/package.json +7 -10
  50. package/dist/index.ts +0 -221
  51. package/dist/src/constants.ts +0 -227
  52. package/dist/src/errors.ts +0 -254
  53. package/dist/src/file-utils.ts +0 -483
  54. package/dist/src/file.js +0 -52
  55. package/dist/src/global.d.ts +0 -12
  56. package/dist/src/mod.ts +0 -19
  57. package/dist/src/taglib.ts +0 -961
  58. package/dist/src/types.ts +0 -538
  59. package/dist/src/utils/file.ts +0 -86
  60. package/dist/src/utils/write.ts +0 -66
  61. package/dist/src/wasm-workers.ts +0 -176
  62. package/dist/src/wasm.ts +0 -133
  63. package/dist/src/web-utils.ts +0 -347
  64. package/dist/src/workers.ts +0 -461
  65. package/dist/src/write.js +0 -33
  66. package/index.ts +0 -221
  67. package/lib/taglib/COPYING.LGPL +0 -502
  68. package/lib/taglib/COPYING.MPL +0 -470
  69. package/lib/taglib/README.md +0 -24
  70. package/src/constants.ts +0 -227
  71. package/src/errors.ts +0 -254
  72. package/src/file-utils.ts +0 -483
  73. package/src/global.d.ts +0 -12
  74. package/src/mod.ts +0 -19
  75. package/src/simple.ts +0 -667
  76. package/src/taglib.ts +0 -961
  77. package/src/types.ts +0 -538
  78. package/src/utils/file.ts +0 -86
  79. package/src/utils/write.ts +0 -66
  80. package/src/wasm-workers.ts +0 -176
  81. package/src/wasm.ts +0 -133
  82. package/src/workers.ts +0 -461
@@ -1,176 +0,0 @@
1
- /**
2
- * @fileoverview WebAssembly module interface for Cloudflare Workers
3
- */
4
-
5
- import type { TagLibConfig, TagLibModule } from "./types.ts";
6
- import { TagLibInitializationError } from "./errors.ts";
7
-
8
- // Re-export TagLibModule for convenience
9
- export type { TagLibModule };
10
-
11
- /**
12
- * Default configuration for taglib-wasm module in Workers environment
13
- * Reduced memory limits to fit within Workers constraints
14
- */
15
- const DEFAULT_WORKERS_CONFIG: Required<TagLibConfig> = {
16
- memory: {
17
- initial: 8 * 1024 * 1024, // 8MB (reduced from 16MB)
18
- maximum: 64 * 1024 * 1024, // 64MB (reduced from 256MB)
19
- },
20
- debug: false,
21
- };
22
-
23
- /**
24
- * Load and initialize the TagLib WebAssembly module for Cloudflare Workers
25
- *
26
- * @param wasmBinary - The WebAssembly binary as Uint8Array
27
- * @param config - Optional configuration for the Wasm module
28
- * @returns Promise resolving to initialized TagLib module
29
- *
30
- * @example
31
- * ```typescript
32
- * import wasmBinary from "../build/taglib.wasm";
33
- *
34
- * const taglib = await loadTagLibModuleForWorkers(wasmBinary);
35
- * ```
36
- */
37
- export async function loadTagLibModuleForWorkers(
38
- wasmBinary: Uint8Array,
39
- config: TagLibConfig = {},
40
- ): Promise<TagLibModule> {
41
- const mergedConfig = { ...DEFAULT_WORKERS_CONFIG, ...config };
42
-
43
- // Create Emscripten module configuration for Workers
44
- const moduleConfig = {
45
- wasmBinary,
46
- wasmMemory: new WebAssembly.Memory({
47
- initial: mergedConfig.memory.initial! / (64 * 1024),
48
- maximum: mergedConfig.memory.maximum! / (64 * 1024),
49
- }),
50
- print: mergedConfig.debug ? console.log : () => {},
51
- printErr: mergedConfig.debug ? console.error : () => {},
52
- onRuntimeInitialized: () => {
53
- if (mergedConfig.debug) {
54
- console.log("taglib-wasm module initialized in Workers");
55
- }
56
- },
57
- // Workers-specific settings
58
- locateFile: () => {
59
- // Return empty string since we're providing wasmBinary directly
60
- return "";
61
- },
62
- // Disable file system access
63
- noFSInit: true,
64
- noExitRuntime: true,
65
- };
66
-
67
- try {
68
- // For Workers, we need to use a modified version of the Emscripten output
69
- // that doesn't include Node.js/CommonJS dependencies
70
- const TagLibWasm = await createWorkersCompatibleModule();
71
-
72
- if (typeof TagLibWasm !== "function") {
73
- throw new TagLibInitializationError(
74
- "Failed to load taglib-wasm module for Workers. " +
75
- "The module may not be properly bundled for the Workers environment.",
76
- );
77
- }
78
-
79
- const wasmInstance = await TagLibWasm(moduleConfig);
80
-
81
- // Ensure proper memory arrays are set up
82
- if (!wasmInstance.HEAPU8) {
83
- const buffer = wasmInstance.buffer || wasmInstance.wasmMemory?.buffer;
84
- if (buffer) {
85
- wasmInstance.HEAPU8 = new Uint8Array(buffer);
86
- wasmInstance.HEAP8 = new Int8Array(buffer);
87
- wasmInstance.HEAP16 = new Int16Array(buffer);
88
- wasmInstance.HEAP32 = new Int32Array(buffer);
89
- wasmInstance.HEAPU16 = new Uint16Array(buffer);
90
- wasmInstance.HEAPU32 = new Uint32Array(buffer);
91
- wasmInstance.HEAPF32 = new Float32Array(buffer);
92
- wasmInstance.HEAPF64 = new Float64Array(buffer);
93
- }
94
- }
95
-
96
- return wasmInstance as TagLibModule;
97
- } catch (error) {
98
- if (error instanceof TagLibInitializationError) {
99
- throw error;
100
- }
101
- throw new TagLibInitializationError(
102
- `Failed to load taglib-wasm for Workers: ${(error as Error).message}`,
103
- { error: (error as Error).message },
104
- );
105
- }
106
- }
107
-
108
- /**
109
- * Create a Workers-compatible version of the Emscripten module
110
- * This function loads the Wasm module without Node.js/CommonJS dependencies
111
- */
112
- async function createWorkersCompatibleModule(): Promise<any> {
113
- // In a real Workers environment, you would typically:
114
- // 1. Use a build process to create a Workers-compatible version of taglib.js
115
- // 2. Or inline the essential parts of the Emscripten runtime here
116
- // 3. Or use dynamic import with proper bundling
117
-
118
- // For now, we'll attempt to load the existing module with Workers compatibility
119
- try {
120
- // Try to import the existing module
121
- const wasmModule = await import("../build/taglib-wrapper.js");
122
- return wasmModule.default || wasmModule;
123
- } catch (error) {
124
- // If that fails, provide a fallback implementation
125
- throw new TagLibInitializationError(
126
- "Workers-compatible Wasm module not available. " +
127
- "Please build with Workers target or use a bundler that supports Wasm modules. " +
128
- `Original error: ${(error as Error).message}`,
129
- { error: (error as Error).message },
130
- );
131
- }
132
- }
133
-
134
- /**
135
- * Convert a C string pointer to JavaScript string (Workers-compatible)
136
- */
137
- export function cStringToJS(module: TagLibModule, ptr: number): string {
138
- if (ptr === 0) return "";
139
-
140
- const view = new Uint8Array(module.HEAPU8.buffer, ptr);
141
- let length = 0;
142
- while (view[length] !== 0) length++;
143
-
144
- return new TextDecoder().decode(view.subarray(0, length));
145
- }
146
-
147
- /**
148
- * Convert JavaScript string to C string (Workers-compatible)
149
- */
150
- export function jsToCString(module: TagLibModule, str: string): number {
151
- const encoder = new TextEncoder();
152
- const bytes = encoder.encode(str + "\0");
153
-
154
- // Use allocate if available, otherwise use _malloc
155
- if (module.allocate && module.ALLOC_NORMAL !== undefined) {
156
- return module.allocate(bytes, module.ALLOC_NORMAL);
157
- } else {
158
- const ptr = module._malloc(bytes.length);
159
- module.HEAPU8.set(bytes, ptr);
160
- return ptr;
161
- }
162
- }
163
-
164
- /**
165
- * Utility function to check if we're running in Cloudflare Workers
166
- */
167
- export function isCloudflareWorkers(): boolean {
168
- return (
169
- typeof globalThis !== "undefined" &&
170
- typeof globalThis.caches !== "undefined" &&
171
- typeof globalThis.Request !== "undefined" &&
172
- typeof globalThis.Response !== "undefined" &&
173
- typeof (globalThis as any).process === "undefined" &&
174
- typeof (globalThis as any).Deno === "undefined"
175
- );
176
- }
package/dist/src/wasm.ts DELETED
@@ -1,133 +0,0 @@
1
- /**
2
- * @fileoverview WebAssembly module interface types for Emscripten
3
- */
4
-
5
- // Basic Emscripten module interface
6
- export interface EmscriptenModule {
7
- // Memory
8
- HEAP8: Int8Array;
9
- HEAP16: Int16Array;
10
- HEAP32: Int32Array;
11
- HEAPU8: Uint8Array;
12
- HEAPU16: Uint16Array;
13
- HEAPU32: Uint32Array;
14
- HEAPF32: Float32Array;
15
- HEAPF64: Float64Array;
16
-
17
- // Memory management
18
- _malloc(size: number): number;
19
- _free(ptr: number): void;
20
- allocate?(data: number[] | Uint8Array, allocator: number): number;
21
- ALLOC_NORMAL?: number;
22
-
23
- // String conversion
24
- ccall?(
25
- ident: string,
26
- returnType: string,
27
- argTypes: string[],
28
- args: any[],
29
- ): any;
30
- cwrap?(
31
- ident: string,
32
- returnType: string,
33
- argTypes: string[],
34
- ): (...args: any[]) => any;
35
-
36
- // File system (if enabled)
37
- FS?: any;
38
-
39
- // Runtime
40
- then?(callback: (module: EmscriptenModule) => void): void;
41
- onRuntimeInitialized?: () => void;
42
- }
43
-
44
- // Embind class interfaces
45
- export interface FileHandle {
46
- loadFromBuffer(data: Uint8Array): boolean;
47
- isValid(): boolean;
48
- save(): boolean;
49
- getFormat(): string;
50
- getProperties(): any;
51
- setProperties(props: any): void;
52
- getProperty(key: string): string;
53
- setProperty(key: string, value: string): void;
54
- isMP4(): boolean;
55
- getMP4Item(key: string): string;
56
- setMP4Item(key: string, value: string): void;
57
- removeMP4Item(key: string): void;
58
- getTag(): TagWrapper;
59
- getAudioProperties(): AudioPropertiesWrapper;
60
- }
61
-
62
- export interface TagWrapper {
63
- title(): string;
64
- artist(): string;
65
- album(): string;
66
- comment(): string;
67
- genre(): string;
68
- year(): number;
69
- track(): number;
70
- setTitle(value: string): void;
71
- setArtist(value: string): void;
72
- setAlbum(value: string): void;
73
- setComment(value: string): void;
74
- setGenre(value: string): void;
75
- setYear(value: number): void;
76
- setTrack(value: number): void;
77
- }
78
-
79
- export interface AudioPropertiesWrapper {
80
- lengthInSeconds(): number;
81
- lengthInMilliseconds(): number;
82
- bitrate(): number;
83
- sampleRate(): number;
84
- channels(): number;
85
- }
86
-
87
- export interface TagLibModule extends Omit<EmscriptenModule, "then"> {
88
- // Embind classes
89
- FileHandle: new () => FileHandle;
90
- TagWrapper: new () => TagWrapper;
91
- AudioPropertiesWrapper: new () => AudioPropertiesWrapper;
92
-
93
- // Embind functions
94
- createFileHandle(): FileHandle;
95
-
96
- // C-style functions (optional - used by Workers API)
97
- _taglib_file_new_from_buffer?(ptr: number, size: number): number;
98
- _taglib_file_delete?(fileId: number): void;
99
- _taglib_file_is_valid?(fileId: number): number;
100
- _taglib_file_format?(fileId: number): number;
101
- _taglib_file_tag?(fileId: number): number;
102
- _taglib_file_audioproperties?(fileId: number): number;
103
- _taglib_file_save?(fileId: number): number;
104
-
105
- _taglib_tag_title?(tagPtr: number): number;
106
- _taglib_tag_artist?(tagPtr: number): number;
107
- _taglib_tag_album?(tagPtr: number): number;
108
- _taglib_tag_comment?(tagPtr: number): number;
109
- _taglib_tag_genre?(tagPtr: number): number;
110
- _taglib_tag_year?(tagPtr: number): number;
111
- _taglib_tag_track?(tagPtr: number): number;
112
-
113
- _taglib_tag_set_title?(tagPtr: number, titlePtr: number): void;
114
- _taglib_tag_set_artist?(tagPtr: number, artistPtr: number): void;
115
- _taglib_tag_set_album?(tagPtr: number, albumPtr: number): void;
116
- _taglib_tag_set_comment?(tagPtr: number, commentPtr: number): void;
117
- _taglib_tag_set_genre?(tagPtr: number, genrePtr: number): void;
118
- _taglib_tag_set_year?(tagPtr: number, year: number): void;
119
- _taglib_tag_set_track?(tagPtr: number, track: number): void;
120
-
121
- _taglib_audioproperties_length?(propsPtr: number): number;
122
- _taglib_audioproperties_bitrate?(propsPtr: number): number;
123
- _taglib_audioproperties_samplerate?(propsPtr: number): number;
124
- _taglib_audioproperties_channels?(propsPtr: number): number;
125
- }
126
-
127
- export interface WasmModule extends EmscriptenModule {
128
- // Alias for compatibility
129
- FileHandle?: new () => FileHandle;
130
- createFileHandle?(): FileHandle;
131
- }
132
-
133
- // Module loading function removed for modular imports
@@ -1,347 +0,0 @@
1
- /**
2
- * @fileoverview Web browser utilities for working with cover art in taglib-wasm
3
- *
4
- * This module provides browser-specific helpers for integrating taglib-wasm
5
- * with web applications, including canvas integration and data URL support.
6
- *
7
- * @example
8
- * ```typescript
9
- * import { pictureToDataURL, setCoverArtFromCanvas } from "taglib-wasm/web-utils";
10
- *
11
- * // Display cover art in an <img> element
12
- * const pictures = await readPictures("song.mp3");
13
- * if (pictures.length > 0) {
14
- * const dataURL = pictureToDataURL(pictures[0]);
15
- * document.getElementById('cover').src = dataURL;
16
- * }
17
- *
18
- * // Set cover art from a canvas
19
- * const canvas = document.getElementById('myCanvas') as HTMLCanvasElement;
20
- * const modifiedBuffer = await setCoverArtFromCanvas("song.mp3", canvas);
21
- * ```
22
- */
23
-
24
- import type { Picture } from "./types.ts";
25
- import { PictureType } from "./types.ts";
26
- import { applyPictures, readPictures } from "./simple.ts";
27
-
28
- /**
29
- * Convert a Picture object to a data URL for display in web browsers
30
- *
31
- * @param picture - Picture object from taglib-wasm
32
- * @returns Data URL string that can be used as src for <img> elements
33
- *
34
- * @example
35
- * ```typescript
36
- * const pictures = await readPictures("song.mp3");
37
- * const imgElement = document.getElementById('coverArt') as HTMLImageElement;
38
- * imgElement.src = pictureToDataURL(pictures[0]);
39
- * ```
40
- */
41
- export function pictureToDataURL(picture: Picture): string {
42
- // Convert Uint8Array to base64
43
- const base64 = btoa(String.fromCharCode(...picture.data));
44
- return `data:${picture.mimeType};base64,${base64}`;
45
- }
46
-
47
- /**
48
- * Convert a data URL to a Picture object
49
- *
50
- * @param dataURL - Data URL string (e.g., "data:image/jpeg;base64,...")
51
- * @param type - Picture type (defaults to FrontCover)
52
- * @param description - Optional description
53
- * @returns Picture object
54
- *
55
- * @example
56
- * ```typescript
57
- * const dataURL = canvas.toDataURL('image/jpeg');
58
- * const picture = dataURLToPicture(dataURL, PictureType.FrontCover);
59
- * const modifiedBuffer = await applyPictures("song.mp3", [picture]);
60
- * ```
61
- */
62
- export function dataURLToPicture(
63
- dataURL: string,
64
- type: PictureType = PictureType.FrontCover,
65
- description?: string,
66
- ): Picture {
67
- // Parse data URL
68
- const matches = dataURL.match(/^data:([^;]+);base64,(.+)$/);
69
- if (!matches) {
70
- throw new Error("Invalid data URL format");
71
- }
72
-
73
- const [, mimeType, base64] = matches;
74
-
75
- // Convert base64 to Uint8Array
76
- const binaryString = atob(base64);
77
- const data = new Uint8Array(binaryString.length);
78
- for (let i = 0; i < binaryString.length; i++) {
79
- data[i] = binaryString.charCodeAt(i);
80
- }
81
-
82
- return {
83
- mimeType,
84
- data,
85
- type,
86
- description,
87
- };
88
- }
89
-
90
- /**
91
- * Set cover art from an HTML canvas element
92
- *
93
- * @param file - File path, Uint8Array buffer, ArrayBuffer, or File object
94
- * @param canvas - HTMLCanvasElement containing the image
95
- * @param options - Options for image format and quality
96
- * @returns Modified file buffer with cover art from canvas
97
- *
98
- * @example
99
- * ```typescript
100
- * const canvas = document.getElementById('albumArt') as HTMLCanvasElement;
101
- * const modifiedBuffer = await setCoverArtFromCanvas("song.mp3", canvas, {
102
- * format: 'image/jpeg',
103
- * quality: 0.9
104
- * });
105
- * ```
106
- */
107
- export async function setCoverArtFromCanvas(
108
- file: string | Uint8Array | ArrayBuffer | File,
109
- canvas: HTMLCanvasElement,
110
- options: {
111
- format?: "image/jpeg" | "image/png" | "image/webp";
112
- quality?: number;
113
- type?: PictureType;
114
- description?: string;
115
- } = {},
116
- ): Promise<Uint8Array> {
117
- const {
118
- format = "image/jpeg",
119
- quality = 0.92,
120
- type = PictureType.FrontCover,
121
- description = "Front Cover",
122
- } = options;
123
-
124
- // Convert canvas to data URL
125
- const dataURL = canvas.toDataURL(format, quality);
126
-
127
- // Convert to Picture object
128
- const picture = dataURLToPicture(dataURL, type, description);
129
-
130
- // Apply to file
131
- return applyPictures(file, [picture]);
132
- }
133
-
134
- /**
135
- * Convert canvas to Picture object using blob for better performance
136
- *
137
- * This is more efficient than toDataURL for large images as it avoids
138
- * the base64 encoding/decoding step.
139
- *
140
- * @param canvas - HTMLCanvasElement containing the image
141
- * @param options - Options for image format and quality
142
- * @returns Promise resolving to Picture object
143
- *
144
- * @example
145
- * ```typescript
146
- * const canvas = document.getElementById('albumArt') as HTMLCanvasElement;
147
- * const picture = await canvasToPicture(canvas, {
148
- * format: 'image/png',
149
- * type: PictureType.BackCover
150
- * });
151
- * ```
152
- */
153
- export async function canvasToPicture(
154
- canvas: HTMLCanvasElement,
155
- options: {
156
- format?: "image/jpeg" | "image/png" | "image/webp";
157
- quality?: number;
158
- type?: PictureType;
159
- description?: string;
160
- } = {},
161
- ): Promise<Picture> {
162
- const {
163
- format = "image/jpeg",
164
- quality = 0.92,
165
- type = PictureType.FrontCover,
166
- description,
167
- } = options;
168
-
169
- return new Promise((resolve, reject) => {
170
- canvas.toBlob(
171
- async (blob) => {
172
- if (!blob) {
173
- reject(new Error("Failed to convert canvas to blob"));
174
- return;
175
- }
176
-
177
- // Convert blob to Uint8Array
178
- const arrayBuffer = await blob.arrayBuffer();
179
- const data = new Uint8Array(arrayBuffer);
180
-
181
- resolve({
182
- mimeType: format,
183
- data,
184
- type,
185
- description,
186
- });
187
- },
188
- format,
189
- quality,
190
- );
191
- });
192
- }
193
-
194
- /**
195
- * Load an image file into a Picture object
196
- *
197
- * @param file - File object from <input type="file"> or drag-and-drop
198
- * @param type - Picture type (defaults to FrontCover)
199
- * @param description - Optional description
200
- * @returns Promise resolving to Picture object
201
- *
202
- * @example
203
- * ```typescript
204
- * // From file input
205
- * const input = document.getElementById('fileInput') as HTMLInputElement;
206
- * input.addEventListener('change', async (e) => {
207
- * const file = e.target.files[0];
208
- * const picture = await imageFileToPicture(file);
209
- * const modifiedBuffer = await applyPictures("song.mp3", [picture]);
210
- * });
211
- * ```
212
- */
213
- export async function imageFileToPicture(
214
- file: File,
215
- type: PictureType = PictureType.FrontCover,
216
- description?: string,
217
- ): Promise<Picture> {
218
- const arrayBuffer = await file.arrayBuffer();
219
- const data = new Uint8Array(arrayBuffer);
220
-
221
- return {
222
- mimeType: file.type,
223
- data,
224
- type,
225
- description: description || file.name,
226
- };
227
- }
228
-
229
- /**
230
- * Display a Picture in an HTML img element
231
- *
232
- * @param picture - Picture object from taglib-wasm
233
- * @param imgElement - HTMLImageElement to display the picture in
234
- *
235
- * @example
236
- * ```typescript
237
- * const pictures = await readPictures("song.mp3");
238
- * const img = document.getElementById('coverArt') as HTMLImageElement;
239
- * displayPicture(pictures[0], img);
240
- * ```
241
- */
242
- export function displayPicture(
243
- picture: Picture,
244
- imgElement: HTMLImageElement,
245
- ): void {
246
- // Clean up previous object URL if any
247
- if (imgElement.src.startsWith("blob:")) {
248
- URL.revokeObjectURL(imgElement.src);
249
- }
250
-
251
- // Create blob and object URL
252
- const blob = new Blob([picture.data], { type: picture.mimeType });
253
- const objectURL = URL.createObjectURL(blob);
254
-
255
- // Set the src
256
- imgElement.src = objectURL;
257
-
258
- // Clean up object URL when image is no longer needed
259
- imgElement.addEventListener("load", () => {
260
- // Keep the URL alive for a bit to ensure image is rendered
261
- setTimeout(() => URL.revokeObjectURL(objectURL), 100);
262
- }, { once: true });
263
- }
264
-
265
- /**
266
- * Create a download link for a Picture
267
- *
268
- * @param picture - Picture object to download
269
- * @param filename - Suggested filename for download
270
- * @returns Temporary download URL (remember to revoke it after use)
271
- *
272
- * @example
273
- * ```typescript
274
- * const pictures = await readPictures("song.mp3");
275
- * const downloadUrl = createPictureDownloadURL(pictures[0], "cover.jpg");
276
- *
277
- * const link = document.createElement('a');
278
- * link.href = downloadUrl;
279
- * link.download = "cover.jpg";
280
- * link.click();
281
- *
282
- * // Clean up
283
- * URL.revokeObjectURL(downloadUrl);
284
- * ```
285
- */
286
- export function createPictureDownloadURL(
287
- picture: Picture,
288
- filename: string = "cover",
289
- ): string {
290
- const blob = new Blob([picture.data], { type: picture.mimeType });
291
- return URL.createObjectURL(blob);
292
- }
293
-
294
- /**
295
- * Extract all pictures and create a gallery
296
- *
297
- * @param file - Audio file to extract pictures from
298
- * @param container - HTML element to append gallery items to
299
- * @param options - Gallery display options
300
- *
301
- * @example
302
- * ```typescript
303
- * const galleryDiv = document.getElementById('pictureGallery');
304
- * await createPictureGallery("song.mp3", galleryDiv, {
305
- * className: 'album-art',
306
- * includeDescription: true
307
- * });
308
- * ```
309
- */
310
- export async function createPictureGallery(
311
- file: string | Uint8Array | ArrayBuffer | File,
312
- container: HTMLElement,
313
- options: {
314
- className?: string;
315
- includeDescription?: boolean;
316
- onClick?: (picture: Picture, index: number) => void;
317
- } = {},
318
- ): Promise<void> {
319
- const pictures = await readPictures(file);
320
-
321
- // Clear container
322
- container.innerHTML = "";
323
-
324
- pictures.forEach((picture, index) => {
325
- const wrapper = document.createElement("div");
326
- wrapper.className = options.className || "picture-item";
327
-
328
- const img = document.createElement("img");
329
- displayPicture(picture, img);
330
- img.alt = picture.description || `Picture ${index + 1}`;
331
-
332
- if (options.onClick) {
333
- img.style.cursor = "pointer";
334
- img.addEventListener("click", () => options.onClick!(picture, index));
335
- }
336
-
337
- wrapper.appendChild(img);
338
-
339
- if (options.includeDescription && picture.description) {
340
- const caption = document.createElement("p");
341
- caption.textContent = picture.description;
342
- wrapper.appendChild(caption);
343
- }
344
-
345
- container.appendChild(wrapper);
346
- });
347
- }