taglib-wasm 2.2.0 → 2.2.2

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 (61) hide show
  1. package/dist/disc-folder.d.ts +3 -1
  2. package/dist/disc-folder.d.ts.map +1 -1
  3. package/dist/disc-folder.js +187 -152
  4. package/dist/index.browser.js +7 -4
  5. package/dist/simple.browser.js +22 -9
  6. package/dist/src/folder-api/album-corroborate.d.ts +13 -0
  7. package/dist/src/folder-api/album-corroborate.d.ts.map +1 -0
  8. package/dist/src/folder-api/album-corroborate.js +36 -0
  9. package/dist/src/folder-api/album-discs.d.ts +13 -0
  10. package/dist/src/folder-api/album-discs.d.ts.map +1 -0
  11. package/dist/src/folder-api/album-discs.js +122 -0
  12. package/dist/src/folder-api/album-grouping.d.ts +24 -0
  13. package/dist/src/folder-api/album-grouping.d.ts.map +1 -0
  14. package/dist/src/folder-api/album-grouping.js +118 -0
  15. package/dist/src/folder-api/album-identity.d.ts +27 -0
  16. package/dist/src/folder-api/album-identity.d.ts.map +1 -0
  17. package/dist/src/folder-api/album-identity.js +151 -0
  18. package/dist/src/folder-api/{group-albums.d.ts → album-types.d.ts} +55 -26
  19. package/dist/src/folder-api/album-types.d.ts.map +1 -0
  20. package/dist/src/folder-api/album-types.js +0 -0
  21. package/dist/src/folder-api/folder-disc.d.ts +24 -0
  22. package/dist/src/folder-api/folder-disc.d.ts.map +1 -0
  23. package/dist/src/folder-api/folder-disc.js +169 -0
  24. package/dist/src/folder-api/index.d.ts +3 -1
  25. package/dist/src/folder-api/index.d.ts.map +1 -1
  26. package/dist/src/folder-api/index.js +2 -4
  27. package/dist/src/folder-api/scan-for-albums.d.ts +1 -1
  28. package/dist/src/folder-api/scan-for-albums.d.ts.map +1 -1
  29. package/dist/src/folder-api/scan-for-albums.js +1 -3
  30. package/dist/src/runtime/wasi-adapter/audio-properties.d.ts +20 -0
  31. package/dist/src/runtime/wasi-adapter/audio-properties.d.ts.map +1 -0
  32. package/dist/src/runtime/wasi-adapter/audio-properties.js +84 -0
  33. package/dist/src/runtime/wasi-adapter/file-handle.d.ts +5 -19
  34. package/dist/src/runtime/wasi-adapter/file-handle.d.ts.map +1 -1
  35. package/dist/src/runtime/wasi-adapter/file-handle.js +67 -389
  36. package/dist/src/runtime/wasi-adapter/handle-state.d.ts +28 -0
  37. package/dist/src/runtime/wasi-adapter/handle-state.d.ts.map +1 -0
  38. package/dist/src/runtime/wasi-adapter/handle-state.js +58 -0
  39. package/dist/src/runtime/wasi-adapter/mp4-items.d.ts +34 -0
  40. package/dist/src/runtime/wasi-adapter/mp4-items.d.ts.map +1 -0
  41. package/dist/src/runtime/wasi-adapter/mp4-items.js +80 -0
  42. package/dist/src/runtime/wasi-adapter/property-surface.d.ts +25 -0
  43. package/dist/src/runtime/wasi-adapter/property-surface.d.ts.map +1 -0
  44. package/dist/src/runtime/wasi-adapter/property-surface.js +104 -0
  45. package/dist/src/runtime/wasi-adapter/structured-fields.d.ts +44 -0
  46. package/dist/src/runtime/wasi-adapter/structured-fields.d.ts.map +1 -0
  47. package/dist/src/runtime/wasi-adapter/structured-fields.js +139 -0
  48. package/dist/src/runtime/wasi-adapter/tag-keys.d.ts +28 -0
  49. package/dist/src/runtime/wasi-adapter/tag-keys.d.ts.map +1 -0
  50. package/dist/src/runtime/wasi-adapter/tag-keys.js +46 -0
  51. package/dist/src/simple/batch-operations.d.ts +28 -11
  52. package/dist/src/simple/batch-operations.d.ts.map +1 -1
  53. package/dist/src/simple/batch-operations.js +17 -7
  54. package/dist/src/utils/tag-mapping.d.ts +8 -0
  55. package/dist/src/utils/tag-mapping.d.ts.map +1 -1
  56. package/dist/src/utils/tag-mapping.js +6 -3
  57. package/dist/src/version.d.ts +1 -1
  58. package/dist/src/version.js +1 -1
  59. package/package.json +2 -1
  60. package/dist/src/folder-api/group-albums.d.ts.map +0 -1
  61. package/dist/src/folder-api/group-albums.js +0 -544
@@ -14,6 +14,8 @@
14
14
  * discFolderInfo("CD1"); // { kind: "exact", number: 1, ... }
15
15
  * ```
16
16
  */
17
- export * from "./src/folder-api/group-albums.js";
17
+ export { discFolderInfo } from "./src/folder-api/folder-disc.js";
18
+ export { groupAlbums } from "./src/folder-api/album-grouping.js";
19
+ export type { AlbumDisc, AlbumGroup, AlbumGroupingResult, AlbumGroupItem, AlbumGroupKey, DiscConfidence, DiscFolderInfo, GroupAlbumsOptions, } from "./src/folder-api/album-types.js";
18
20
  export type { FolderScanResult } from "./src/folder-api/types.js";
19
21
  //# sourceMappingURL=disc-folder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"disc-folder.d.ts","sourceRoot":"","sources":["../disc-folder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,cAAc,kCAAkC,CAAC;AACjD,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"disc-folder.d.ts","sourceRoot":"","sources":["../disc-folder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACjE,YAAY,EACV,SAAS,EACT,UAAU,EACV,mBAAmB,EACnB,cAAc,EACd,aAAa,EACb,cAAc,EACd,cAAc,EACd,kBAAkB,GACnB,MAAM,iCAAiC,CAAC;AACzC,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC"}
@@ -1,23 +1,4 @@
1
- // src/utils/path.ts
2
- function basename(path) {
3
- const i = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
4
- return i === -1 ? path : path.slice(i + 1);
5
- }
6
-
7
- // src/folder-api/group-albums.ts
8
- function discFolderInfo(name) {
9
- const parse = parseDiscName(name);
10
- if (!parse) return void 0;
11
- return {
12
- kind: parse.kind,
13
- gated: parse.gated,
14
- number: parse.number,
15
- total: parse.total,
16
- title: parse.title,
17
- discTitle: parse.discTitle,
18
- confidence: parse.gated ? "low" : baseConfidence(parse)
19
- };
20
- }
1
+ // src/folder-api/folder-disc.ts
21
2
  function dirname(path) {
22
3
  const i = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
23
4
  return i === -1 ? path : path.slice(0, i);
@@ -168,6 +149,32 @@ function baseConfidence(parse) {
168
149
  return "low";
169
150
  }
170
151
  }
152
+ function discFolderInfo(name) {
153
+ const parse = parseDiscName(name);
154
+ if (!parse) return void 0;
155
+ return {
156
+ kind: parse.kind,
157
+ gated: parse.gated,
158
+ number: parse.number,
159
+ total: parse.total,
160
+ title: parse.title,
161
+ discTitle: parse.discTitle,
162
+ confidence: parse.gated ? "low" : baseConfidence(parse)
163
+ };
164
+ }
165
+
166
+ // src/utils/path.ts
167
+ function basename(path) {
168
+ const i = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
169
+ return i === -1 ? path : path.slice(i + 1);
170
+ }
171
+
172
+ // src/folder-api/album-corroborate.ts
173
+ var CONFIDENCE_RANK = {
174
+ low: 0,
175
+ medium: 1,
176
+ high: 2
177
+ };
171
178
  function corroborated(parse, siblings) {
172
179
  if (!parse.gated && parse.kind === "exact") return true;
173
180
  if (siblings.length === 0) return false;
@@ -193,13 +200,8 @@ function corroborated(parse, siblings) {
193
200
  }
194
201
  return false;
195
202
  }
196
- function flatPrefix(filename) {
197
- const separated = filename.match(/^(\d{1,2})[\s._-](\d{1,2})/);
198
- if (separated) return { disc: parseInt(separated[1], 10), form: "separated" };
199
- const compact = filename.match(/^(\d)(\d{2})(?:\D|$)/);
200
- if (compact) return { disc: parseInt(compact[1], 10), form: "compact" };
201
- return void 0;
202
- }
203
+
204
+ // src/folder-api/album-identity.ts
203
205
  function normalizeKey(s) {
204
206
  return s.trim().toLowerCase().replace(/\s+/g, " ");
205
207
  }
@@ -208,134 +210,63 @@ function isGenericAlbumArtist(albumArtist) {
208
210
  const n = normalizeKey(albumArtist);
209
211
  return n === "various artists" || n === "va";
210
212
  }
211
- var CONFIDENCE_RANK = {
212
- low: 0,
213
- medium: 1,
214
- high: 2
215
- };
216
- function groupAlbums(result, options = {}) {
217
- const minRank = CONFIDENCE_RANK[options.minFolderConfidence ?? "low"];
218
- const useFlatPrefixes = options.flatDiscPrefixes ?? true;
219
- const useFolderFallback = options.folderFallback ?? true;
220
- const okItems = [];
221
- const errors = [];
222
- for (const item of result.items) {
223
- if (item.status === "ok") {
224
- okItems.push({ path: item.path, tags: item.tags, metadata: item });
225
- } else {
226
- errors.push({ path: item.path, error: item.error });
227
- }
228
- }
229
- if (okItems.length === 0) {
230
- return { albums: [], singles: [], unmatched: [], errors };
213
+ function evidenceFor(item, confirmedDiscs, flatSubdivisions2) {
214
+ const dir = dirname(item.path);
215
+ const own = confirmedDiscs.get(dir);
216
+ if (own) {
217
+ return {
218
+ folderDiscNumber: own.parse.number,
219
+ total: own.parse.total,
220
+ folderDiscTitle: own.parse.discTitle,
221
+ confidence: own.confidence,
222
+ albumDir: dirname(dir)
223
+ };
231
224
  }
232
- const scanRoot = options.scanRoot;
233
- const tree = /* @__PURE__ */ new Map();
234
- for (const item of okItems) {
235
- const dir = dirname(item.path);
236
- let node = tree.get(dir);
237
- if (!node) {
238
- node = { files: [], parent: dirname(dir) };
239
- tree.set(dir, node);
225
+ let cursor = dirname(dir);
226
+ while (cursor !== void 0) {
227
+ const entry = confirmedDiscs.get(cursor);
228
+ if (entry) {
229
+ return {
230
+ folderDiscNumber: entry.parse.number,
231
+ total: entry.parse.total,
232
+ folderDiscTitle: entry.parse.discTitle,
233
+ confidence: entry.confidence,
234
+ albumDir: dirname(cursor)
235
+ };
240
236
  }
241
- node.files.push(item);
237
+ const parent = dirname(cursor);
238
+ if (parent === cursor) break;
239
+ cursor = parent;
242
240
  }
243
- const confirmedDiscs = /* @__PURE__ */ new Map();
244
- for (const [dir, node] of tree) {
245
- if (node.files.length === 0) continue;
246
- const parse = parseDiscName(basename(dir));
247
- if (!parse) continue;
248
- const siblings = [...tree.keys()].filter(
249
- (d) => d !== dir && dirname(d) === dirname(dir)
250
- );
251
- const corroboratedBySiblings = corroborated(parse, siblings);
252
- let confidence = parse.gated ? "low" : baseConfidence(parse);
253
- if (parse.gated) {
254
- if (!corroboratedBySiblings) continue;
255
- } else if (parse.kind === "bonus" || parse.kind === "bare") {
256
- if (!corroboratedBySiblings) continue;
257
- } else if (corroboratedBySiblings) {
258
- confidence = "high";
259
- }
260
- if (CONFIDENCE_RANK[confidence] < minRank) continue;
261
- confirmedDiscs.set(dir, { parse, confidence });
241
+ const flat = flatSubdivisions2.get(dir)?.get(item.path);
242
+ if (flat !== void 0) {
243
+ return {
244
+ folderDiscNumber: flat,
245
+ total: void 0,
246
+ folderDiscTitle: void 0,
247
+ confidence: "medium",
248
+ albumDir: dir
249
+ };
262
250
  }
263
- const flatSubdivisions = /* @__PURE__ */ new Map();
264
- if (useFlatPrefixes) {
265
- for (const [dir, node] of tree) {
266
- if (node.files.length === 0 || confirmedDiscs.has(dir)) continue;
267
- const byFile = /* @__PURE__ */ new Map();
268
- const forms = /* @__PURE__ */ new Set();
269
- for (const item of node.files) {
270
- const prefix = flatPrefix(basename(item.path));
271
- if (prefix) {
272
- byFile.set(item.path, prefix.disc);
273
- forms.add(prefix.form);
274
- }
275
- }
276
- if (byFile.size < 2) continue;
277
- const distinct = [...new Set(byFile.values())].sort((a, b) => a - b);
278
- const allCompact = forms.size === 1 && forms.has("compact");
279
- const plausible = allCompact ? distinct.every((d) => d >= 1 && d <= distinct.length) : true;
280
- if (plausible) flatSubdivisions.set(dir, byFile);
281
- }
251
+ return void 0;
252
+ }
253
+ function folderIdentity(dir, confirmedDiscs, scanRoot) {
254
+ const own = confirmedDiscs.get(dir);
255
+ if (!own) {
256
+ const title2 = basename(dir);
257
+ return title2 ? { title: title2, titleSource: dir } : void 0;
258
+ }
259
+ if (own.parse.title !== void 0) {
260
+ return { title: own.parse.title, titleSource: dirname(dir) };
282
261
  }
262
+ const source = dirname(dir);
263
+ if (source === scanRoot || source === dir) return void 0;
264
+ const title = basename(source);
265
+ return title ? { title, titleSource: source } : void 0;
266
+ }
267
+ function buildAlbumIdentity(okItems, confirmedDiscs, flatSubdivisions2, options) {
283
268
  const albums = /* @__PURE__ */ new Map();
284
269
  const unmatched = [];
285
- const evidenceFor = (item) => {
286
- const dir = dirname(item.path);
287
- const own = confirmedDiscs.get(dir);
288
- if (own) {
289
- return {
290
- folderDiscNumber: own.parse.number,
291
- total: own.parse.total,
292
- folderDiscTitle: own.parse.discTitle,
293
- confidence: own.confidence,
294
- albumDir: dirname(dir)
295
- };
296
- }
297
- let cursor = dirname(dir);
298
- while (cursor !== void 0) {
299
- const entry = confirmedDiscs.get(cursor);
300
- if (entry) {
301
- return {
302
- folderDiscNumber: entry.parse.number,
303
- total: entry.parse.total,
304
- folderDiscTitle: entry.parse.discTitle,
305
- confidence: entry.confidence,
306
- albumDir: dirname(cursor)
307
- };
308
- }
309
- const parent = dirname(cursor);
310
- if (parent === cursor) break;
311
- cursor = parent;
312
- }
313
- const flat = flatSubdivisions.get(dir)?.get(item.path);
314
- if (flat !== void 0) {
315
- return {
316
- folderDiscNumber: flat,
317
- total: void 0,
318
- folderDiscTitle: void 0,
319
- confidence: "medium",
320
- albumDir: dir
321
- };
322
- }
323
- return void 0;
324
- };
325
- const folderIdentity = (dir) => {
326
- const own = confirmedDiscs.get(dir);
327
- if (!own) {
328
- const title2 = basename(dir);
329
- return title2 ? { title: title2, titleSource: dir } : void 0;
330
- }
331
- if (own.parse.title !== void 0) {
332
- return { title: own.parse.title, titleSource: dirname(dir) };
333
- }
334
- const source = dirname(dir);
335
- if (source === scanRoot || source === dir) return void 0;
336
- const title = basename(source);
337
- return title ? { title, titleSource: source } : void 0;
338
- };
339
270
  for (const item of okItems) {
340
271
  const dir = dirname(item.path);
341
272
  const albumTag = item.tags.album?.[0]?.trim();
@@ -356,7 +287,7 @@ function groupAlbums(result, options = {}) {
356
287
  };
357
288
  albums.set(key2, acc2);
358
289
  }
359
- const evidence2 = evidenceFor(item);
290
+ const evidence2 = evidenceFor(item, confirmedDiscs, flatSubdivisions2);
360
291
  acc2.evidence.set(item.path, evidence2);
361
292
  acc2.items.push({
362
293
  ...item.metadata,
@@ -365,11 +296,11 @@ function groupAlbums(result, options = {}) {
365
296
  });
366
297
  continue;
367
298
  }
368
- if (!useFolderFallback) {
299
+ if (!options.useFolderFallback) {
369
300
  unmatched.push(item.metadata);
370
301
  continue;
371
302
  }
372
- const identity = folderIdentity(dir);
303
+ const identity = folderIdentity(dir, confirmedDiscs, options.scanRoot);
373
304
  if (!identity) {
374
305
  unmatched.push(item.metadata);
375
306
  continue;
@@ -388,7 +319,7 @@ function groupAlbums(result, options = {}) {
388
319
  };
389
320
  albums.set(key, acc);
390
321
  }
391
- const evidence = evidenceFor(item);
322
+ const evidence = evidenceFor(item, confirmedDiscs, flatSubdivisions2);
392
323
  acc.evidence.set(item.path, evidence);
393
324
  acc.items.push({
394
325
  ...item.metadata,
@@ -415,6 +346,11 @@ function groupAlbums(result, options = {}) {
415
346
  break;
416
347
  }
417
348
  }
349
+ return { albums, unmatched };
350
+ }
351
+
352
+ // src/folder-api/album-discs.ts
353
+ function assembleDiscs(albums) {
418
354
  const groups = [];
419
355
  for (const acc of albums.values()) {
420
356
  const dirTagCommon = /* @__PURE__ */ new Map();
@@ -530,6 +466,105 @@ function groupAlbums(result, options = {}) {
530
466
  items: sortedDiscs.flatMap((d) => d.items)
531
467
  });
532
468
  }
469
+ return groups;
470
+ }
471
+
472
+ // src/folder-api/album-grouping.ts
473
+ function flatPrefix(filename) {
474
+ const separated = filename.match(/^(\d{1,2})[\s._-](\d{1,2})/);
475
+ if (separated) return { disc: parseInt(separated[1], 10), form: "separated" };
476
+ const compact = filename.match(/^(\d)(\d{2})(?:\D|$)/);
477
+ if (compact) return { disc: parseInt(compact[1], 10), form: "compact" };
478
+ return void 0;
479
+ }
480
+ function normalizeOk(result) {
481
+ const okItems = [];
482
+ const errors = [];
483
+ for (const item of result.items) {
484
+ if (item.status === "ok") {
485
+ okItems.push({ path: item.path, tags: item.tags, metadata: item });
486
+ } else {
487
+ errors.push({ path: item.path, error: item.error });
488
+ }
489
+ }
490
+ return { okItems, errors };
491
+ }
492
+ function buildTree(okItems) {
493
+ const tree = /* @__PURE__ */ new Map();
494
+ for (const item of okItems) {
495
+ const dir = dirname(item.path);
496
+ let node = tree.get(dir);
497
+ if (!node) {
498
+ node = { files: [], parent: dirname(dir) };
499
+ tree.set(dir, node);
500
+ }
501
+ node.files.push(item);
502
+ }
503
+ return tree;
504
+ }
505
+ function confirmDiscs(tree, minRank) {
506
+ const confirmedDiscs = /* @__PURE__ */ new Map();
507
+ for (const [dir, node] of tree) {
508
+ if (node.files.length === 0) continue;
509
+ const parse = parseDiscName(basename(dir));
510
+ if (!parse) continue;
511
+ const siblings = [...tree.keys()].filter(
512
+ (d) => d !== dir && dirname(d) === dirname(dir)
513
+ );
514
+ const corroboratedBySiblings = corroborated(parse, siblings);
515
+ let confidence = parse.gated ? "low" : baseConfidence(parse);
516
+ if (parse.gated) {
517
+ if (!corroboratedBySiblings) continue;
518
+ } else if (parse.kind === "bonus" || parse.kind === "bare") {
519
+ if (!corroboratedBySiblings) continue;
520
+ } else if (corroboratedBySiblings) {
521
+ confidence = "high";
522
+ }
523
+ if (CONFIDENCE_RANK[confidence] < minRank) continue;
524
+ confirmedDiscs.set(dir, { parse, confidence });
525
+ }
526
+ return confirmedDiscs;
527
+ }
528
+ function flatSubdivisions(tree, confirmedDiscs, useFlatPrefixes) {
529
+ const flat = /* @__PURE__ */ new Map();
530
+ if (!useFlatPrefixes) return flat;
531
+ for (const [dir, node] of tree) {
532
+ if (node.files.length === 0 || confirmedDiscs.has(dir)) continue;
533
+ const byFile = /* @__PURE__ */ new Map();
534
+ const forms = /* @__PURE__ */ new Set();
535
+ for (const item of node.files) {
536
+ const prefix = flatPrefix(basename(item.path));
537
+ if (prefix) {
538
+ byFile.set(item.path, prefix.disc);
539
+ forms.add(prefix.form);
540
+ }
541
+ }
542
+ if (byFile.size < 2) continue;
543
+ const distinct = [...new Set(byFile.values())].sort((a, b) => a - b);
544
+ const allCompact = forms.size === 1 && forms.has("compact");
545
+ const plausible = allCompact ? distinct.every((d) => d >= 1 && d <= distinct.length) : true;
546
+ if (plausible) flat.set(dir, byFile);
547
+ }
548
+ return flat;
549
+ }
550
+ function groupAlbums(result, options = {}) {
551
+ const minRank = CONFIDENCE_RANK[options.minFolderConfidence ?? "low"];
552
+ const useFlatPrefixes = options.flatDiscPrefixes ?? true;
553
+ const useFolderFallback = options.folderFallback ?? true;
554
+ const { okItems, errors } = normalizeOk(result);
555
+ if (okItems.length === 0) {
556
+ return { albums: [], singles: [], unmatched: [], errors };
557
+ }
558
+ const tree = buildTree(okItems);
559
+ const confirmedDiscs = confirmDiscs(tree, minRank);
560
+ const flat = flatSubdivisions(tree, confirmedDiscs, useFlatPrefixes);
561
+ const { albums, unmatched } = buildAlbumIdentity(
562
+ okItems,
563
+ confirmedDiscs,
564
+ flat,
565
+ { scanRoot: options.scanRoot, useFolderFallback }
566
+ );
567
+ const groups = assembleDiscs(albums);
533
568
  const albumsOut = [];
534
569
  const singles = [];
535
570
  for (const group of groups) {
@@ -2879,14 +2879,16 @@ function mapPropertiesToExtendedTag(props) {
2879
2879
  }
2880
2880
  return tag;
2881
2881
  }
2882
- function mergeTagUpdates(file, tags) {
2883
- const currentProps = file.properties();
2882
+ function mergeTagUpdatesInto(currentProps, tags) {
2884
2883
  const newProps = normalizeTagInput(tags);
2885
2884
  reconcilePairFields(currentProps, newProps, tags);
2886
2885
  if (newProps.date !== void 0 || newProps.year !== void 0) {
2887
2886
  currentProps.YEAR = [];
2888
2887
  }
2889
- file.setProperties({ ...currentProps, ...newProps });
2888
+ return { ...currentProps, ...newProps };
2889
+ }
2890
+ function mergeTagUpdates(file, tags) {
2891
+ file.setProperties(mergeTagUpdatesInto(file.properties(), tags));
2890
2892
  }
2891
2893
  function reconcilePairFields(currentProps, newProps, input) {
2892
2894
  const pairs = [
@@ -3002,7 +3004,7 @@ var VERSION;
3002
3004
  var init_version = __esm({
3003
3005
  "src/version.ts"() {
3004
3006
  "use strict";
3005
- VERSION = "2.2.0";
3007
+ VERSION = "2.2.2";
3006
3008
  }
3007
3009
  });
3008
3010
 
@@ -3492,6 +3494,7 @@ async function readPictureMetadata(file) {
3492
3494
  // src/simple/batch-operations.ts
3493
3495
  init_audio_formats();
3494
3496
  init_errors2();
3497
+ init_properties();
3495
3498
  init_tag_mapping();
3496
3499
  async function executeBatch(files, options, processor) {
3497
3500
  if (files.length === 0) return { items: [], duration: 0 };
@@ -2811,14 +2811,16 @@ function mapPropertiesToExtendedTag(props) {
2811
2811
  }
2812
2812
  return tag;
2813
2813
  }
2814
- function mergeTagUpdates(file, tags) {
2815
- const currentProps = file.properties();
2814
+ function mergeTagUpdatesInto(currentProps, tags) {
2816
2815
  const newProps = normalizeTagInput(tags);
2817
2816
  reconcilePairFields(currentProps, newProps, tags);
2818
2817
  if (newProps.date !== void 0 || newProps.year !== void 0) {
2819
2818
  currentProps.YEAR = [];
2820
2819
  }
2821
- file.setProperties({ ...currentProps, ...newProps });
2820
+ return { ...currentProps, ...newProps };
2821
+ }
2822
+ function mergeTagUpdates(file, tags) {
2823
+ file.setProperties(mergeTagUpdatesInto(file.properties(), tags));
2822
2824
  }
2823
2825
  function reconcilePairFields(currentProps, newProps, input) {
2824
2826
  const pairs = [
@@ -2934,7 +2936,7 @@ var VERSION;
2934
2936
  var init_version = __esm({
2935
2937
  "src/version.ts"() {
2936
2938
  "use strict";
2937
- VERSION = "2.2.0";
2939
+ VERSION = "2.2.2";
2938
2940
  }
2939
2941
  });
2940
2942
 
@@ -3418,6 +3420,7 @@ async function readPictureMetadata(file) {
3418
3420
  // src/simple/batch-operations.ts
3419
3421
  init_audio_formats();
3420
3422
  init_errors2();
3423
+ init_properties();
3421
3424
  init_tag_mapping();
3422
3425
  async function executeBatch(files, options, processor) {
3423
3426
  if (files.length === 0) return { items: [], duration: 0 };
@@ -3569,16 +3572,26 @@ async function executeWriteBatch(entries, options, writer) {
3569
3572
  }
3570
3573
  async function writeTagsBatch(updates, options = {}) {
3571
3574
  const byPath = new Map(updates.map((u) => [u.path, u]));
3572
- return executeWriteBatch(updates, options, (path) => {
3573
- const update = byPath.get(path);
3574
- return applyTagsToFile(path, update.tags);
3575
- });
3575
+ return executeWriteBatch(
3576
+ updates,
3577
+ options,
3578
+ (path) => withAudioFileSaveToFile(path, (audioFile) => {
3579
+ const update = byPath.get(path);
3580
+ const merged = update.tags !== void 0 ? mergeTagUpdatesInto(audioFile.properties(), update.tags) : audioFile.properties();
3581
+ if (update.properties !== void 0) {
3582
+ for (const [key, values] of Object.entries(update.properties)) {
3583
+ merged[fromTagLibKey(key)] = values;
3584
+ }
3585
+ }
3586
+ audioFile.setProperties(merged);
3587
+ })
3588
+ );
3576
3589
  }
3577
3590
  async function editTagsBatch(files, mutator, options = {}) {
3578
3591
  return executeWriteBatch(
3579
3592
  files,
3580
3593
  options,
3581
- (path) => withAudioFileSaveToFile(path, mutator)
3594
+ (path) => withAudioFileSaveToFile(path, (audioFile) => mutator(audioFile, path))
3582
3595
  );
3583
3596
  }
3584
3597
 
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Sibling corroboration for gated disc-folder names (taglib-ys7m split).
3
+ *
4
+ * Gated names (title-word markers, single-letter tokens, bonus, bare) are
5
+ * discs only when a sibling under the same parent confirms them — an
6
+ * unconditional disc sibling, or sibling numbering of their own (a folder
7
+ * never confirms itself). Side-letter tokens corroborate each other
8
+ * ("CD D" + "CD E").
9
+ */
10
+ import type { DiscConfidence, DiscParse } from "./album-types.js";
11
+ export declare const CONFIDENCE_RANK: Record<DiscConfidence, number>;
12
+ export declare function corroborated(parse: DiscParse, siblings: string[]): boolean;
13
+ //# sourceMappingURL=album-corroborate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"album-corroborate.d.ts","sourceRoot":"","sources":["../../../src/folder-api/album-corroborate.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElE,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAI1D,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,OAAO,CAoC1E"}
@@ -0,0 +1,36 @@
1
+ import { basename } from "../utils/path.js";
2
+ import { parseDiscName } from "./folder-disc.js";
3
+ const CONFIDENCE_RANK = {
4
+ low: 0,
5
+ medium: 1,
6
+ high: 2
7
+ };
8
+ function corroborated(parse, siblings) {
9
+ if (!parse.gated && parse.kind === "exact") return true;
10
+ if (siblings.length === 0) return false;
11
+ const siblingParses = siblings.map((s) => parseDiscName(basename(s))).filter((p) => p !== void 0);
12
+ const hasExactSibling = siblingParses.some(
13
+ (p) => !p.gated && p.kind === "exact"
14
+ );
15
+ const hasNumberedSibling = siblingParses.some(
16
+ (p) => p.number !== void 0
17
+ );
18
+ if (parse.number !== void 0 && hasNumberedSibling) return true;
19
+ if (!parse.gated && parse.kind === "embedded" && hasExactSibling) return true;
20
+ if (!parse.gated && parse.kind === "volume" && hasNumberedSibling) {
21
+ return true;
22
+ }
23
+ if (parse.sideLetter) {
24
+ return siblingParses.some((p) => p.sideLetter);
25
+ }
26
+ if (parse.kind === "bonus" || parse.kind === "bare") {
27
+ const numberedCount = siblingParses.filter((p) => p.number !== void 0).length;
28
+ if (numberedCount >= 2) return true;
29
+ if (parse.number !== void 0 && hasNumberedSibling) return true;
30
+ }
31
+ return false;
32
+ }
33
+ export {
34
+ CONFIDENCE_RANK,
35
+ corroborated
36
+ };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Disc assembly and finalization (spec steps 6-7, taglib-ys7m split).
3
+ *
4
+ * Each file resolves to a disc number: the common tag value among its
5
+ * directory's files (missing abstains, disagreement falls back to the
6
+ * folder), else the folder evidence (own disc parse, ancestor, or flat
7
+ * filename prefix). Files resolving to the SAME number merge into one
8
+ * AlbumDisc, with tagDiscNumber recomputed over the merged set.
9
+ */
10
+ import type { AlbumAccumulator, AlbumGroup } from "./album-types.js";
11
+ /** Assemble discs per album, then sort discs and items (steps 6-7). */
12
+ export declare function assembleDiscs(albums: Map<AlbumAccumulator["key"], AlbumAccumulator>): AlbumGroup[];
13
+ //# sourceMappingURL=album-discs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"album-discs.d.ts","sourceRoot":"","sources":["../../../src/folder-api/album-discs.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EACV,gBAAgB,EAEhB,UAAU,EAEX,MAAM,kBAAkB,CAAC;AAE1B,uEAAuE;AACvE,wBAAgB,aAAa,CAC3B,MAAM,EAAE,GAAG,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,gBAAgB,CAAC,GACrD,UAAU,EAAE,CAoKd"}