playlist-data-engine 1.6.0 → 1.7.1

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 (33) hide show
  1. package/dist/analysis.js +1 -1
  2. package/dist/analysis.mjs +2 -2
  3. package/dist/arweaveGatewayManager-C9SIMDTd.cjs +1 -0
  4. package/dist/{arweaveGatewayManager-BKUZ1Klm.js → arweaveGatewayManager-DDSO5sLJ.js} +381 -338
  5. package/dist/core/parser/MetadataExtractor.d.ts +57 -3
  6. package/dist/core/parser/MetadataExtractor.d.ts.map +1 -1
  7. package/dist/core/parser/PlaylistParser.d.ts.map +1 -1
  8. package/dist/core/parser/TrackExtras.d.ts +23 -0
  9. package/dist/core/parser/TrackExtras.d.ts.map +1 -1
  10. package/dist/core/types/Playlist.d.ts +4 -1
  11. package/dist/core/types/Playlist.d.ts.map +1 -1
  12. package/dist/gateway-DYTdioNs.js +677 -0
  13. package/dist/gateway-UffqbE1i.cjs +1 -0
  14. package/dist/gateway.d.ts +2 -0
  15. package/dist/gateway.d.ts.map +1 -1
  16. package/dist/gateway.js +1 -1
  17. package/dist/gateway.mjs +18 -315
  18. package/dist/index.d.ts +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/playlist-data-engine.js +40 -40
  21. package/dist/playlist-data-engine.mjs +1742 -1973
  22. package/dist/{unifyBeatMap-sIbIw5OX.cjs → unifyBeatMap-DIsNlck4.cjs} +1 -1
  23. package/dist/{unifyBeatMap-BA5EG991.js → unifyBeatMap-k6vw_JPQ.js} +1 -1
  24. package/dist/utils/arweaveGatewayManager.d.ts +18 -3
  25. package/dist/utils/arweaveGatewayManager.d.ts.map +1 -1
  26. package/dist/utils/arweaveUtils.d.ts +29 -0
  27. package/dist/utils/arweaveUtils.d.ts.map +1 -1
  28. package/dist/utils/ipfsUtils.d.ts +7 -0
  29. package/dist/utils/ipfsUtils.d.ts.map +1 -1
  30. package/dist/utils/playlistUtils.d.ts +2 -0
  31. package/dist/utils/playlistUtils.d.ts.map +1 -1
  32. package/package.json +34 -31
  33. package/dist/arweaveGatewayManager-CeW9SP2P.cjs +0 -1
@@ -0,0 +1,677 @@
1
+ import "./arweaveGatewayManager-DDSO5sLJ.js";
2
+ class n {
3
+ /**
4
+ * Extract primary (compressed) audio URL with priority:
5
+ * 1. mp3_url (Standard web audio - preferred)
6
+ * 2. lossy_audio (Compressed)
7
+ * 3. audio_url (Explicit audio field)
8
+ * 4. audio (Bare audio field — some platforms use this instead of audio_url)
9
+ * 5. lossless_audio (High fidelity - larger files)
10
+ * 6. animation_url (OpenSea standard - often audio, but could be video)
11
+ * 7. multimedia_url (Alternative media field)
12
+ */
13
+ static extractAudioUrl(t) {
14
+ const e = ["mp3_url", "mp3Url", "lossy_audio", "lossyAudio", "audio_url", "audioUrl", "audio", "lossless_audio", "losslessAudio", "animation_url", "animationUrl", "multimedia_url", "multimediaUrl"];
15
+ for (const r of e)
16
+ if (t[r] && typeof t[r] == "string")
17
+ return t[r];
18
+ return null;
19
+ }
20
+ /**
21
+ * Extract the highest-fidelity audio URL the track offers — best
22
+ * available, not a guarantee. A track whose only source is an mp3 gets
23
+ * that back rather than null.
24
+ */
25
+ static extractAudioUrlLossless(t) {
26
+ const e = ["lossless_audio", "losslessAudio", "wav_url", "wavUrl", "flac_url", "flacUrl", "audio_url", "audioUrl"];
27
+ for (const r of e)
28
+ if (t[r] && typeof t[r] == "string")
29
+ return t[r];
30
+ return null;
31
+ }
32
+ /**
33
+ * Extract image URL with priority:
34
+ *
35
+ * Flat fields:
36
+ * 1. image_small (Preferred for performance)
37
+ * 2. image (Standard)
38
+ * 3. image_url (Explicit image URL variant)
39
+ * 4. image_large (Fallback)
40
+ * 5. image_uri (URI variant)
41
+ * 6. image_preview (Preview variant)
42
+ * 7. image_thumb (Last resort)
43
+ *
44
+ * Nested object fields (checked if no flat match found):
45
+ * 8. artwork.uri (Nina Protocol structured artwork)
46
+ * 9. project.artwork.uri (Project-level artwork)
47
+ * 10. primaryMedia.uri (Primary media field)
48
+ */
49
+ static extractImageUrl(t) {
50
+ const e = ["image_small", "imageSmall", "image", "image_large", "imageLarge", "image_thumb", "imageThumb", "image_url", "imageUrl", "image_uri", "imageUri", "image_preview", "imagePreview"];
51
+ for (const s of e)
52
+ if (t[s] && typeof t[s] == "string")
53
+ return t[s];
54
+ const r = [
55
+ this.extractNestedUri(t, "artwork", "uri"),
56
+ this.extractNestedUri(t, "project", "artwork", "uri"),
57
+ this.extractNestedUri(t, "primaryMedia", "uri")
58
+ ];
59
+ for (const s of r)
60
+ if (s) return s;
61
+ return null;
62
+ }
63
+ /**
64
+ * Extract thumbnail image URL directly from image_thumb fields.
65
+ * Unlike extractImageUrl, this specifically targets thumbnail fields only.
66
+ * Priority: image_thumb_url > image_thumb
67
+ */
68
+ static extractImageThumbUrl(t) {
69
+ return t.image_thumb_url && typeof t.image_thumb_url == "string" ? t.image_thumb_url : t.image_thumb && typeof t.image_thumb == "string" ? t.image_thumb : null;
70
+ }
71
+ /**
72
+ * Extract name/title with priority (see specs/001-core-engine/SPEC.md):
73
+ * 1. name
74
+ * 2. title
75
+ */
76
+ static extractTitle(t) {
77
+ return t.name && typeof t.name == "string" ? t.name : t.title && typeof t.title == "string" ? t.title : null;
78
+ }
79
+ /**
80
+ * Extract artist with priority (see specs/001-core-engine/SPEC.md):
81
+ * 1. artist
82
+ * 2. created_by
83
+ * 3. minter
84
+ * 4. OpenSea-style attributes array with trait_type "Artist"
85
+ */
86
+ static extractArtist(t) {
87
+ const e = ["artist", "artist_name", "artistName", "album_artist", "albumArtist", "created_by", "createdBy", "minter"];
88
+ for (const r of e)
89
+ if (t[r] && typeof t[r] == "string")
90
+ return t[r];
91
+ if (Array.isArray(t.attributes)) {
92
+ const r = t.attributes.find(
93
+ (s) => s.trait_type?.toLowerCase() === "artist" && s.value !== void 0
94
+ );
95
+ if (r && typeof r.value == "string")
96
+ return r.value;
97
+ }
98
+ return null;
99
+ }
100
+ /**
101
+ * Parse metadata string to JSON object
102
+ * Handles stringified JSON with error handling
103
+ */
104
+ static parseMetadata(t) {
105
+ if (!t)
106
+ return null;
107
+ if (typeof t == "object" && !Array.isArray(t))
108
+ return t;
109
+ if (typeof t == "string")
110
+ try {
111
+ const e = JSON.parse(t);
112
+ if (typeof e == "object" && !Array.isArray(e))
113
+ return e;
114
+ } catch (e) {
115
+ return console.warn("Failed to parse metadata:", e), null;
116
+ }
117
+ return null;
118
+ }
119
+ /**
120
+ * Extract genre with support for string or array formats.
121
+ * Sources are checked in priority order; first non-empty wins:
122
+ * - `genre` field as a string
123
+ * - `genre` field as an array (first element)
124
+ * - OpenSea `attributes` array with `trait_type` of "genre" (case-insensitive)
125
+ * - `attributes` as an object keyed by trait name (e.g. `attributes.Genre`)
126
+ * - malformed `attribute ` key (literal trailing space) with `.genre`
127
+ * Stays strict: only fields literally named "genre" are considered
128
+ * (no fallback to `category` or similar).
129
+ */
130
+ static extractGenre(t) {
131
+ if (typeof t.genre == "string")
132
+ return t.genre;
133
+ if (Array.isArray(t.genre) && t.genre.length > 0) {
134
+ const r = t.genre[0];
135
+ return typeof r == "string" ? r : "";
136
+ }
137
+ if (Array.isArray(t.attributes)) {
138
+ const r = t.attributes.find(
139
+ (s) => s.trait_type?.toLowerCase() === "genre" && s.value !== void 0
140
+ );
141
+ if (r) {
142
+ if (typeof r.value == "string")
143
+ return r.value;
144
+ if (Array.isArray(r.value) && r.value.length > 0) {
145
+ const s = r.value[0];
146
+ return typeof s == "string" ? s : "";
147
+ }
148
+ }
149
+ }
150
+ if (t.attributes && typeof t.attributes == "object" && !Array.isArray(t.attributes)) {
151
+ const r = t.attributes;
152
+ for (const s of Object.keys(r))
153
+ if (s.toLowerCase() === "genre" && typeof r[s] == "string")
154
+ return r[s];
155
+ }
156
+ const e = t["attribute "];
157
+ if (e && typeof e == "object" && !Array.isArray(e)) {
158
+ const r = e.genre;
159
+ if (typeof r == "string")
160
+ return r;
161
+ }
162
+ return "";
163
+ }
164
+ /**
165
+ * Extract tags from track metadata, normalized to a deduped,
166
+ * lowercased, trimmed string[] with empties removed.
167
+ * Sources (concatenated, then deduped):
168
+ * - `tags` field as an array, or a single comma-separated string
169
+ * - `properties.tags` array (when `properties` is an object)
170
+ * - OpenSea `attributes` array with `trait_type` of "tag"/"tags" (case-insensitive)
171
+ * - `attributes` as an object keyed by trait name (e.g. `attributes.Tags`)
172
+ */
173
+ static extractTags(t) {
174
+ const e = [];
175
+ if (Array.isArray(t.tags) ? e.push(...t.tags) : typeof t.tags == "string" && e.push(...t.tags.split(",")), t.properties && typeof t.properties == "object" && !Array.isArray(t.properties)) {
176
+ const r = t.properties.tags;
177
+ Array.isArray(r) ? e.push(...r) : typeof r == "string" && e.push(...r.split(","));
178
+ }
179
+ if (Array.isArray(t.attributes))
180
+ for (const r of t.attributes) {
181
+ if (!r || typeof r != "object") continue;
182
+ const s = r.trait_type;
183
+ if (typeof s == "string" && s.toLowerCase().match(/^tags?$/)) {
184
+ const o = r.value;
185
+ Array.isArray(o) ? e.push(...o) : typeof o == "string" && e.push(...o.split(","));
186
+ }
187
+ }
188
+ else if (t.attributes && typeof t.attributes == "object") {
189
+ const r = t.attributes;
190
+ for (const s of Object.keys(r))
191
+ if (s.toLowerCase().match(/^tags?$/)) {
192
+ const o = r[s];
193
+ Array.isArray(o) ? e.push(...o) : typeof o == "string" && e.push(...o.split(","));
194
+ }
195
+ }
196
+ return n.normalizeTags(e);
197
+ }
198
+ /**
199
+ * Extract track description with priority:
200
+ *
201
+ * Flat fields:
202
+ * 1. description (Canonical)
203
+ * 2. track_description / trackDescription (Explicit track-scoped variants)
204
+ * 3. description_text / descriptionText (Text-suffixed variants)
205
+ * 4. desc / blurb (Short-form variants)
206
+ *
207
+ * Then:
208
+ * 5. OpenSea-style attributes array with trait_type "description" (case-insensitive)
209
+ * 6. Deep search fallback — breadth-first walk of nested objects for the
210
+ * shallowest key matching /description/i (catches properties.description
211
+ * and any other nested home for the field). Arrays are skipped: items
212
+ * inside arrays (stems, mixes, attribute entries) carry their own scoped
213
+ * descriptions, not the track's.
214
+ */
215
+ static extractDescription(t) {
216
+ const e = ["description", "track_description", "trackDescription", "description_text", "descriptionText", "desc", "blurb"];
217
+ for (const r of e)
218
+ if (t[r] && typeof t[r] == "string")
219
+ return t[r];
220
+ if (Array.isArray(t.attributes)) {
221
+ const r = t.attributes.find(
222
+ (s) => s.trait_type?.toLowerCase() === "description" && typeof s.value == "string"
223
+ );
224
+ if (r && typeof r.value == "string")
225
+ return r.value;
226
+ }
227
+ return n.deepFindString(t, /description/i);
228
+ }
229
+ /**
230
+ * Extract album description — one of the bonus flavors. Priority:
231
+ *
232
+ * Flat fields:
233
+ * 1. album_description (Canonical)
234
+ * 2. albumDescription (camelCase variant)
235
+ *
236
+ * Nested object fields (only when `album` is an object — it is just as
237
+ * often a plain name string, which yields nothing here):
238
+ * 3. Deep search scoped to the album object — catches album.description,
239
+ * album.liner_notes, album.notes and friends without ever misattributing
240
+ * the track's own description to the album.
241
+ */
242
+ static extractAlbumDescription(t) {
243
+ const e = ["album_description", "albumDescription"];
244
+ for (const s of e)
245
+ if (t[s] && typeof t[s] == "string")
246
+ return t[s];
247
+ const r = t.album;
248
+ return r && typeof r == "object" && !Array.isArray(r) ? n.deepFindString(r, /description|liner|notes/i) : null;
249
+ }
250
+ /**
251
+ * Extract artist description — one of the bonus flavors. Priority:
252
+ *
253
+ * Flat fields:
254
+ * 1. artist_description (Canonical)
255
+ * 2. artistDescription (camelCase variant)
256
+ * 3. artist_bio / artistBio (Bio variants)
257
+ *
258
+ * Nested object fields (only when `artist` is an object — it is just as
259
+ * often a plain name string, which yields nothing here):
260
+ * 4. Deep search scoped to the artist object — catches artist.description,
261
+ * artist.bio, artist.about and friends without ever misattributing the
262
+ * track's own description to the artist.
263
+ */
264
+ static extractArtistDescription(t) {
265
+ const e = ["artist_description", "artistDescription", "artist_bio", "artistBio"];
266
+ for (const s of e)
267
+ if (t[s] && typeof t[s] == "string")
268
+ return t[s];
269
+ const r = t.artist;
270
+ return r && typeof r == "object" && !Array.isArray(r) ? n.deepFindString(r, /description|bio|about/i) : null;
271
+ }
272
+ /**
273
+ * Breadth-first search for the shallowest string value whose key matches
274
+ * the given pattern. First match in breadth order wins: shallowest level,
275
+ * then key order. Objects are descended (arrays skipped), depth-capped so
276
+ * pathological metadata blobs stay cheap.
277
+ */
278
+ static deepFindString(t, e, r = 5) {
279
+ const s = /* @__PURE__ */ new Set(), o = [{ node: t, depth: 0 }];
280
+ for (; o.length > 0; ) {
281
+ const { node: u, depth: c } = o.shift();
282
+ for (const f of Object.keys(u)) {
283
+ const a = u[f];
284
+ if (typeof a == "string" && a && e.test(f))
285
+ return a;
286
+ }
287
+ if (!(c >= r))
288
+ for (const f of Object.keys(u)) {
289
+ const a = u[f];
290
+ a && typeof a == "object" && !Array.isArray(a) && !s.has(a) && (s.add(a), o.push({ node: a, depth: c + 1 }));
291
+ }
292
+ }
293
+ return null;
294
+ }
295
+ /**
296
+ * Normalize a list of raw tag values into a deduped, lowercased,
297
+ * trimmed string[] with empties removed.
298
+ */
299
+ static normalizeTags(t) {
300
+ const e = /* @__PURE__ */ new Set();
301
+ for (const r of t) {
302
+ if (r == null) continue;
303
+ const s = String(r).trim().toLowerCase();
304
+ s && e.add(s);
305
+ }
306
+ return Array.from(e);
307
+ }
308
+ static convertAttributes(t) {
309
+ if (!Array.isArray(t))
310
+ return null;
311
+ const e = {};
312
+ for (const r of t)
313
+ r.trait_type && r.value !== void 0 && (e[r.trait_type] = r.value);
314
+ return Object.keys(e).length > 0 ? e : null;
315
+ }
316
+ /**
317
+ * Safely extract a URI string from a nested object path.
318
+ * e.g. extractNestedUri(data, 'project', 'artwork', 'uri') => data.project.artwork.uri
319
+ */
320
+ static extractNestedUri(t, ...e) {
321
+ let r = t;
322
+ for (const s of e)
323
+ if (r && typeof r == "object" && !Array.isArray(r))
324
+ r = r[s];
325
+ else
326
+ return null;
327
+ return typeof r == "string" ? r : null;
328
+ }
329
+ }
330
+ function g(i) {
331
+ if ("audio_url" in i && typeof i.audio_url == "string")
332
+ return i.audio_url;
333
+ if ("metadata" in i) {
334
+ const t = n.parseMetadata(i.metadata);
335
+ if (t)
336
+ return n.extractAudioUrl(t);
337
+ }
338
+ return null;
339
+ }
340
+ function A(i) {
341
+ if ("audio_url_lossless" in i && typeof i.audio_url_lossless == "string")
342
+ return i.audio_url_lossless;
343
+ if ("metadata" in i) {
344
+ const t = n.parseMetadata(i.metadata);
345
+ if (t)
346
+ return n.extractAudioUrlLossless(t);
347
+ }
348
+ return null;
349
+ }
350
+ function d(i) {
351
+ if ("image_url" in i && typeof i.image_url == "string")
352
+ return i.image_url;
353
+ if ("metadata" in i) {
354
+ if ("artwork_url" in i && typeof i.artwork_url == "string")
355
+ return i.artwork_url;
356
+ const t = n.parseMetadata(i.metadata);
357
+ if (t)
358
+ return n.extractImageUrl(t);
359
+ }
360
+ return null;
361
+ }
362
+ function _(i) {
363
+ if ("image_thumb_url" in i && typeof i.image_thumb_url == "string")
364
+ return i.image_thumb_url;
365
+ if ("metadata" in i) {
366
+ const t = n.parseMetadata(i.metadata);
367
+ if (t)
368
+ return n.extractImageThumbUrl(t);
369
+ }
370
+ return null;
371
+ }
372
+ function h(i) {
373
+ if ("metadata" in i) {
374
+ const t = n.parseMetadata(i.metadata);
375
+ if (t && typeof t.vrm == "string" && t.vrm)
376
+ return t.vrm;
377
+ }
378
+ return "extras" in i && i.extras && typeof i.extras.vrm == "string" && i.extras.vrm || null;
379
+ }
380
+ function M(i) {
381
+ const t = [];
382
+ for (const e of i.tracks) {
383
+ const r = g(e);
384
+ r && t.push(r);
385
+ }
386
+ return t;
387
+ }
388
+ function j(i) {
389
+ const t = [];
390
+ for (const e of i.tracks) {
391
+ const r = d(e);
392
+ r && t.push(r);
393
+ }
394
+ return t;
395
+ }
396
+ function F(i) {
397
+ const t = [];
398
+ for (const e of i.tracks) {
399
+ if ("title" in e && typeof e.title == "string") {
400
+ t.push(e.title);
401
+ continue;
402
+ }
403
+ if ("metadata" in e) {
404
+ const r = n.parseMetadata(e.metadata);
405
+ if (r) {
406
+ const s = n.extractTitle(r);
407
+ s && t.push(s);
408
+ }
409
+ }
410
+ }
411
+ return t;
412
+ }
413
+ function $(i) {
414
+ const t = [];
415
+ for (const e of i.tracks) {
416
+ if ("artist" in e && typeof e.artist == "string") {
417
+ t.push(e.artist);
418
+ continue;
419
+ }
420
+ if ("metadata" in e) {
421
+ const r = n.parseMetadata(e.metadata);
422
+ if (r) {
423
+ const s = n.extractArtist(r);
424
+ s && t.push(s);
425
+ }
426
+ }
427
+ }
428
+ return t;
429
+ }
430
+ function D(i) {
431
+ const t = /* @__PURE__ */ new Set();
432
+ for (const e of i.tracks) {
433
+ if ("genre" in e && typeof e.genre == "string" && e.genre) {
434
+ t.add(e.genre);
435
+ continue;
436
+ }
437
+ if ("metadata" in e) {
438
+ const r = n.parseMetadata(e.metadata);
439
+ r && typeof r.genre == "string" && r.genre && t.add(r.genre);
440
+ }
441
+ }
442
+ return Array.from(t).sort();
443
+ }
444
+ function I(i) {
445
+ const t = /* @__PURE__ */ new Set();
446
+ for (const e of i.tracks) {
447
+ if ("tags" in e && Array.isArray(e.tags)) {
448
+ for (const r of e.tags)
449
+ r && t.add(r.toLowerCase());
450
+ continue;
451
+ }
452
+ if ("metadata" in e) {
453
+ const r = n.parseMetadata(e.metadata);
454
+ if (r && Array.isArray(r.tags))
455
+ for (const s of r.tags)
456
+ s && t.add(String(s).toLowerCase());
457
+ }
458
+ }
459
+ return Array.from(t).sort();
460
+ }
461
+ function C(i) {
462
+ let t = 0;
463
+ for (const e of i.tracks) {
464
+ if ("duration" in e && typeof e.duration == "number") {
465
+ t += e.duration;
466
+ continue;
467
+ }
468
+ if ("metadata" in e) {
469
+ const r = n.parseMetadata(e.metadata);
470
+ r && typeof r.duration == "number" && (t += r.duration);
471
+ }
472
+ }
473
+ return t;
474
+ }
475
+ function H(i) {
476
+ return i.tracks.length;
477
+ }
478
+ function P(i) {
479
+ const t = [];
480
+ for (const e of i.tracks) {
481
+ const r = g(e), s = d(e), o = _(e);
482
+ let u = "", c = "", f;
483
+ if ("title" in e)
484
+ u = e.title || "", c = e.artist || "", f = e.description || void 0;
485
+ else if ("metadata" in e) {
486
+ const a = n.parseMetadata(e.metadata);
487
+ a && (u = n.extractTitle(a) || "", c = n.extractArtist(a) || "", f = n.extractDescription(a) || void 0);
488
+ }
489
+ if (r) {
490
+ const a = {
491
+ title: u,
492
+ artist: c,
493
+ audio_url: r,
494
+ image_url: s || ""
495
+ }, l = A(e);
496
+ l && l !== r && (a.audio_url_lossless = l), o && (a.image_thumb_url = o), f && (a.description = f), "audio_ipfs_hash" in e && typeof e.audio_ipfs_hash == "string" && (a.audio_ipfs_hash = e.audio_ipfs_hash), "artwork_ipfs_hash" in e && typeof e.artwork_ipfs_hash == "string" && (a.artwork_ipfs_hash = e.artwork_ipfs_hash), t.push(a);
497
+ }
498
+ }
499
+ return t;
500
+ }
501
+ function W(i) {
502
+ const t = [];
503
+ for (const e of i.tracks) {
504
+ if ("audio_url" in e) {
505
+ t.push({ ...e });
506
+ continue;
507
+ }
508
+ if ("metadata" in e) {
509
+ const r = n.parseMetadata(e.metadata);
510
+ if (r) {
511
+ const s = n.extractAudioUrlLossless(r), o = n.extractAudioUrl(r);
512
+ t.push({
513
+ id: e.chain_name === "AR" ? `AR-${e.tx_id}` : `${e.chain_name}-${e.token_address}-${e.token_id}`,
514
+ chain_name: e.chain_name,
515
+ token_address: e.token_address,
516
+ token_id: e.token_id,
517
+ tx_id: e.tx_id,
518
+ platform: e.platform,
519
+ title: n.extractTitle(r),
520
+ artist: n.extractArtist(r),
521
+ audio_url: o,
522
+ image_url: n.extractImageUrl(r),
523
+ image_thumb_url: n.extractImageThumbUrl(r),
524
+ duration: r.duration,
525
+ genre: r.genre,
526
+ tags: r.tags,
527
+ bpm: r.bpm,
528
+ key: r.key,
529
+ album: r.album,
530
+ description: n.extractDescription(r),
531
+ attributes: n.convertAttributes(r.attributes),
532
+ ...s && s !== o ? { audio_url_lossless: s } : {},
533
+ // v0.4 IPFS hash fields from raw track (mint fields live only in metadata interior)
534
+ ...e.audio_ipfs_hash ? { audio_ipfs_hash: e.audio_ipfs_hash } : {},
535
+ ...e.artwork_ipfs_hash ? { artwork_ipfs_hash: e.artwork_ipfs_hash } : {}
536
+ });
537
+ }
538
+ }
539
+ }
540
+ return t;
541
+ }
542
+ function E(i) {
543
+ const t = [];
544
+ for (const e of i.tracks) {
545
+ const r = h(e);
546
+ r && t.push(r);
547
+ }
548
+ return t;
549
+ }
550
+ function N(i) {
551
+ const t = [];
552
+ for (const e of i.tracks) {
553
+ const r = h(e);
554
+ if (!r) continue;
555
+ const s = g(e), o = d(e), u = _(e);
556
+ let c = "", f = "", a;
557
+ if ("title" in e)
558
+ c = e.title || "", f = e.artist || "", a = e.description || void 0;
559
+ else if ("metadata" in e) {
560
+ const p = n.parseMetadata(e.metadata);
561
+ p && (c = n.extractTitle(p) || "", f = n.extractArtist(p) || "", a = n.extractDescription(p) || void 0);
562
+ }
563
+ const l = {
564
+ title: c,
565
+ artist: f,
566
+ audio_url: s || "",
567
+ image_url: o || "",
568
+ vrm: r
569
+ };
570
+ u && (l.image_thumb_url = u), a && (l.description = a), t.push(l);
571
+ }
572
+ return t;
573
+ }
574
+ const y = [
575
+ "ipfs.io",
576
+ "cloudflare-ipfs.com",
577
+ "dweb.link",
578
+ "gateway.pinata.cloud",
579
+ "ipfs.infura.io",
580
+ "nftstorage.link",
581
+ "web3-music-pipeline.mypinata.cloud",
582
+ "soundxyz.mypinata.cloud",
583
+ "gold-broad-gibbon-29.mypinata.cloud",
584
+ "bonfire.mypinata.cloud",
585
+ "spinamp.mypinata.cloud",
586
+ "catalogworks.b-cdn.net",
587
+ "www.heds.cloud"
588
+ ], k = "ipfs.io", m = "/ipfs/", w = /^Qm[1-9A-HJ-NP-Za-km-z]{44}$/, x = /^b[a-z2-7]{55,}$/;
589
+ function v(i) {
590
+ return w.test(i) || x.test(i);
591
+ }
592
+ const T = /^ipfs_[a-z]+$/;
593
+ function b(i) {
594
+ const t = i.split("/").filter(Boolean);
595
+ for (let e = 0; e < t.length - 1; e++)
596
+ if (T.test(t[e]) && v(t[e + 1])) {
597
+ const r = t[e + 1], s = t.slice(e + 2);
598
+ return s.length > 0 ? `${r}/${s.join("/")}` : r;
599
+ }
600
+ return null;
601
+ }
602
+ function U(i) {
603
+ if (!i || typeof i != "string") return null;
604
+ if (i.startsWith("ipfs://ipfs/"))
605
+ return i.slice(12);
606
+ if (i.startsWith("ipfs://"))
607
+ return i.slice(7);
608
+ try {
609
+ const t = new URL(i), { hostname: e, pathname: r } = t, s = e.match(/^([A-Za-z0-9]+)\.ipfs\.dweb\.link$/);
610
+ if (s)
611
+ return `${s[1]}${r}`;
612
+ if (y.some(
613
+ (c) => e === c || e.endsWith("." + c)
614
+ ) && r.startsWith(m))
615
+ return r.slice(m.length);
616
+ const u = b(r);
617
+ if (u) return u;
618
+ } catch {
619
+ }
620
+ return null;
621
+ }
622
+ function R(i) {
623
+ if (!i || typeof i != "string") return !1;
624
+ if (i.startsWith("ipfs://")) return !0;
625
+ try {
626
+ const t = new URL(i), { hostname: e, pathname: r } = t;
627
+ if (/^[A-Za-z0-9]+\.ipfs\.dweb\.link$/.test(e) || y.some(
628
+ (o) => e === o || e.endsWith("." + o)
629
+ ) && r.startsWith(m) || b(r)) return !0;
630
+ } catch {
631
+ }
632
+ return !1;
633
+ }
634
+ function S(i) {
635
+ if (!(!i || i.startsWith("ipfs://")))
636
+ try {
637
+ const { hostname: t } = new URL(i);
638
+ return y.some(
639
+ (r) => t === r || t.endsWith("." + r)
640
+ ) ? t : void 0;
641
+ } catch {
642
+ return;
643
+ }
644
+ }
645
+ function G(i, t = {}) {
646
+ if (!i) return i;
647
+ const e = typeof t == "string" ? { gatewayHost: t } : t, { gatewayHost: r = k, preserveOriginalGateway: s = !1 } = e, o = U(i);
648
+ if (o) {
649
+ if (s) {
650
+ const u = S(i);
651
+ if (u)
652
+ return `https://${u}/ipfs/${o}`;
653
+ }
654
+ return `https://${r}/ipfs/${o}`;
655
+ }
656
+ return i;
657
+ }
658
+ export {
659
+ k as D,
660
+ y as K,
661
+ n as M,
662
+ M as a,
663
+ W as b,
664
+ D as c,
665
+ j as d,
666
+ U as e,
667
+ I as f,
668
+ $ as g,
669
+ C as h,
670
+ H as i,
671
+ F as j,
672
+ P as k,
673
+ N as l,
674
+ E as m,
675
+ R as n,
676
+ G as r
677
+ };