playlist-data-engine 1.7.1 → 1.7.3
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/README.md +6 -3
- package/dist/core/parser/PlaylistParser.d.ts.map +1 -1
- package/dist/core/parser/TrackExtras.d.ts +150 -1
- package/dist/core/parser/TrackExtras.d.ts.map +1 -1
- package/dist/core/types/Playlist.d.ts +8 -0
- package/dist/core/types/Playlist.d.ts.map +1 -1
- package/dist/gateway-C_p9Ku3O.js +1211 -0
- package/dist/gateway-Ceg-5xug.cjs +1 -0
- package/dist/gateway.d.ts +2 -1
- package/dist/gateway.d.ts.map +1 -1
- package/dist/gateway.js +1 -1
- package/dist/gateway.mjs +19 -14
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/playlist-data-engine.js +34 -34
- package/dist/playlist-data-engine.mjs +966 -1245
- package/dist/utils/playlistUtils.d.ts +41 -0
- package/dist/utils/playlistUtils.d.ts.map +1 -1
- package/dist/utils/validators.d.ts +27 -0
- package/dist/utils/validators.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/gateway-DYTdioNs.js +0 -677
- package/dist/gateway-UffqbE1i.cjs +0 -1
|
@@ -0,0 +1,1211 @@
|
|
|
1
|
+
import { a as F } from "./arweaveGatewayManager-DDSO5sLJ.js";
|
|
2
|
+
class l {
|
|
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 n = r.value;
|
|
185
|
+
Array.isArray(n) ? e.push(...n) : typeof n == "string" && e.push(...n.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 n = r[s];
|
|
193
|
+
Array.isArray(n) ? e.push(...n) : typeof n == "string" && e.push(...n.split(","));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return l.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 l.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) ? l.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) ? l.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(), n = [{ node: t, depth: 0 }];
|
|
280
|
+
for (; n.length > 0; ) {
|
|
281
|
+
const { node: c, depth: a } = n.shift();
|
|
282
|
+
for (const o of Object.keys(c)) {
|
|
283
|
+
const u = c[o];
|
|
284
|
+
if (typeof u == "string" && u && e.test(o))
|
|
285
|
+
return u;
|
|
286
|
+
}
|
|
287
|
+
if (!(a >= r))
|
|
288
|
+
for (const o of Object.keys(c)) {
|
|
289
|
+
const u = c[o];
|
|
290
|
+
u && typeof u == "object" && !Array.isArray(u) && !s.has(u) && (s.add(u), n.push({ node: u, depth: a + 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 lt(i) {
|
|
331
|
+
if (!i || !i.metadata) return null;
|
|
332
|
+
if (typeof i.metadata == "string")
|
|
333
|
+
try {
|
|
334
|
+
const t = JSON.parse(i.metadata);
|
|
335
|
+
return typeof t == "object" && t !== null && !Array.isArray(t) ? t : null;
|
|
336
|
+
} catch {
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
return typeof i.metadata == "object" && !Array.isArray(i.metadata) ? i.metadata : null;
|
|
340
|
+
}
|
|
341
|
+
function h(i) {
|
|
342
|
+
const t = { stems: [], mixes: [], hasExtras: !1 };
|
|
343
|
+
if (!i) return t;
|
|
344
|
+
const e = V(i), r = Y(i), s = typeof i.vrm == "string" ? i.vrm : void 0, n = G(i.lyrics), c = b(i.visualizer), a = b(i.video), o = O(i.merch), u = K(i.credits), p = typeof i.midi == "string" ? i.midi : void 0, f = typeof i.step_mania == "string" ? i.step_mania : void 0, m = typeof i.clone_hero == "string" ? i.clone_hero : void 0, d = typeof i.external_url == "string" ? i.external_url : void 0;
|
|
345
|
+
return {
|
|
346
|
+
hasExtras: e.length > 0 || r.length > 0 || !!s || !!n || !!c || !!a || !!o || !!u || !!p || !!f || !!m || !!d,
|
|
347
|
+
...e.length > 0 ? { stems: e } : {},
|
|
348
|
+
...r.length > 0 ? { mixes: r } : {},
|
|
349
|
+
...s ? { vrm: s } : {},
|
|
350
|
+
...n ? { lyrics: n } : {},
|
|
351
|
+
...c ? { visualizer: c } : {},
|
|
352
|
+
...a ? { video: a } : {},
|
|
353
|
+
...o ? { merch: o } : {},
|
|
354
|
+
...u ? { credits: u } : {},
|
|
355
|
+
...p ? { midi: p } : {},
|
|
356
|
+
...f ? { step_mania: f } : {},
|
|
357
|
+
...m ? { clone_hero: m } : {},
|
|
358
|
+
...d ? { external_url: d } : {}
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
const w = ["audio/wav", "audio/x-wav", "audio/flac"];
|
|
362
|
+
function M(i, t, e) {
|
|
363
|
+
const r = t?.["Selected Mix"], s = typeof i == "string" && i || typeof r == "string" && r || null;
|
|
364
|
+
if (!s || s === "default") return null;
|
|
365
|
+
const n = (e ?? []).filter((u) => u.name === s);
|
|
366
|
+
if (n.length === 0) return null;
|
|
367
|
+
const c = (u) => w.includes((u.mime_type ?? "").toLowerCase()), a = n.find(c), o = n.find((u) => !c(u)) ?? n[0];
|
|
368
|
+
return {
|
|
369
|
+
name: s,
|
|
370
|
+
...o?.uri ? { audio_url: o.uri } : {},
|
|
371
|
+
...a?.uri && a.uri !== o?.uri ? { audio_url_lossless: a.uri } : {}
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
function ft(i, t, e) {
|
|
375
|
+
if (!i || !t) return null;
|
|
376
|
+
const r = S(i, t, e?.caseInsensitive !== !1, e?.aliases !== !1);
|
|
377
|
+
return r.length > 0 ? T(r, e?.prefer ?? "lossy") : null;
|
|
378
|
+
}
|
|
379
|
+
function T(i, t = "lossy") {
|
|
380
|
+
if (!i || i.length === 0) return null;
|
|
381
|
+
if (i.length === 1) return i[0];
|
|
382
|
+
const e = t === "lossy" ? ["audio/mpeg", "audio/wav", "audio/flac"] : ["audio/wav", "audio/flac", "audio/mpeg"];
|
|
383
|
+
for (const r of e) {
|
|
384
|
+
const s = i.find((n) => $(n.mime_type) === r);
|
|
385
|
+
if (s) return s;
|
|
386
|
+
}
|
|
387
|
+
return i[0];
|
|
388
|
+
}
|
|
389
|
+
function pt(i) {
|
|
390
|
+
if (!i || !Array.isArray(i)) return [];
|
|
391
|
+
const t = /* @__PURE__ */ new Map();
|
|
392
|
+
for (const e of i) {
|
|
393
|
+
const r = e.name.trim().toLowerCase(), s = t.get(r);
|
|
394
|
+
s ? s.push(e) : t.set(r, [e]);
|
|
395
|
+
}
|
|
396
|
+
return Array.from(t.values()).map((e) => ({
|
|
397
|
+
name: e[0].name,
|
|
398
|
+
mixes: e
|
|
399
|
+
}));
|
|
400
|
+
}
|
|
401
|
+
async function A(i, t) {
|
|
402
|
+
if (!i) return null;
|
|
403
|
+
const e = i, r = e.uri ?? e.audio_url ?? e.audio_url_lossless;
|
|
404
|
+
if (!r) return null;
|
|
405
|
+
let s = r;
|
|
406
|
+
if (t?.followMetadata !== !1 && R(i, r)) {
|
|
407
|
+
const n = await B(r, t?.fetchTimeoutMs);
|
|
408
|
+
if (!n) return null;
|
|
409
|
+
s = n;
|
|
410
|
+
}
|
|
411
|
+
if (t?.resolveUrl === !1) return s;
|
|
412
|
+
try {
|
|
413
|
+
return await F.resolveUrl(s);
|
|
414
|
+
} catch {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
async function mt(i, t, e) {
|
|
419
|
+
const r = i.extras?.mixes;
|
|
420
|
+
if (!r || r.length === 0 || !t) return null;
|
|
421
|
+
let s = S(r, t, !0, e?.aliases !== !1);
|
|
422
|
+
if (s.length === 0) return null;
|
|
423
|
+
if (e?.conditionsContext) {
|
|
424
|
+
const p = Z(
|
|
425
|
+
{ hasExtras: i.extras?.hasExtras ?? !0, mixes: r },
|
|
426
|
+
e.conditionsContext
|
|
427
|
+
), f = new Set(p.filter((m) => m.allMet).map((m) => m.mix.name));
|
|
428
|
+
if (s = s.filter((m) => f.has(m.name)), s.length === 0) return null;
|
|
429
|
+
}
|
|
430
|
+
const n = T(s, e?.prefer ?? "lossy");
|
|
431
|
+
if (!n || !n.uri) return null;
|
|
432
|
+
const c = {
|
|
433
|
+
resolveUrl: e?.resolveUrl,
|
|
434
|
+
followMetadata: e?.followMetadata
|
|
435
|
+
}, a = await A(n, c);
|
|
436
|
+
if (!a) return null;
|
|
437
|
+
const o = s.find(P), u = o && o !== n && o.uri ? await A(o, c) : void 0;
|
|
438
|
+
return {
|
|
439
|
+
...i,
|
|
440
|
+
audio_url: a,
|
|
441
|
+
audio_url_lossless: u && u !== a ? u : void 0,
|
|
442
|
+
selected_mix: n.name
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
const D = ["application/json", "application/ld+json"], N = {
|
|
446
|
+
"audio/x-wav": "audio/wav",
|
|
447
|
+
"audio/wave": "audio/wav",
|
|
448
|
+
"audio/vnd.wave": "audio/wav",
|
|
449
|
+
"audio/x-flac": "audio/flac",
|
|
450
|
+
"audio/mp3": "audio/mpeg",
|
|
451
|
+
"audio/mpeg3": "audio/mpeg"
|
|
452
|
+
};
|
|
453
|
+
function $(i) {
|
|
454
|
+
const t = (i ?? "").trim().toLowerCase();
|
|
455
|
+
return N[t] ?? t;
|
|
456
|
+
}
|
|
457
|
+
function P(i) {
|
|
458
|
+
return w.includes($(i.mime_type));
|
|
459
|
+
}
|
|
460
|
+
function W(i, t, e) {
|
|
461
|
+
if (!i || !t) return [];
|
|
462
|
+
const r = (n) => e ? n.trim().toLowerCase() : n.trim(), s = r(t);
|
|
463
|
+
return i.filter((n) => r(n.name) === s);
|
|
464
|
+
}
|
|
465
|
+
const H = [
|
|
466
|
+
["no vocals", "instrumental"],
|
|
467
|
+
["vocals only", "acapella"],
|
|
468
|
+
["vocal only", "acapella"],
|
|
469
|
+
["a cappella", "acapella"],
|
|
470
|
+
["tv mix", "karaoke"],
|
|
471
|
+
["tv version", "karaoke"],
|
|
472
|
+
["karaoke version", "karaoke"],
|
|
473
|
+
["karaoke mix", "karaoke"]
|
|
474
|
+
], z = {
|
|
475
|
+
inst: "instrumental",
|
|
476
|
+
instr: "instrumental",
|
|
477
|
+
instrument: "instrumental",
|
|
478
|
+
instrumentals: "instrumental",
|
|
479
|
+
aca: "acapella",
|
|
480
|
+
acap: "acapella",
|
|
481
|
+
acappella: "acapella",
|
|
482
|
+
cappella: "acapella",
|
|
483
|
+
vox: "acapella",
|
|
484
|
+
vocal: "acapella",
|
|
485
|
+
vocals: "acapella",
|
|
486
|
+
rmx: "remix",
|
|
487
|
+
tvmix: "karaoke",
|
|
488
|
+
tv: "karaoke",
|
|
489
|
+
karoake: "karaoke"
|
|
490
|
+
};
|
|
491
|
+
function x(i) {
|
|
492
|
+
let t = " " + i.toLowerCase().replace(/[^a-z0-9\s]/g, " ").replace(/\s+/g, " ").trim() + " ";
|
|
493
|
+
for (const [e, r] of H)
|
|
494
|
+
t = t.split(" " + e + " ").join(" " + r + " ");
|
|
495
|
+
return t.trim().split(" ").filter(Boolean).map((e) => z[e] ?? e);
|
|
496
|
+
}
|
|
497
|
+
function S(i, t, e, r) {
|
|
498
|
+
const s = W(i, t, e);
|
|
499
|
+
if (s.length > 0 || !r || !e) return s;
|
|
500
|
+
const n = x(t);
|
|
501
|
+
return n.length === 0 ? [] : i.filter((c) => {
|
|
502
|
+
const a = x(c.name);
|
|
503
|
+
return n.every((o) => a.includes(o));
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
function R(i, t) {
|
|
507
|
+
const e = ("mime_type" in i ? i.mime_type : "") ?? "";
|
|
508
|
+
return D.includes(e.toLowerCase()) ? !0 : e ? !1 : t.split(/[?#]/)[0].toLowerCase().endsWith(".json");
|
|
509
|
+
}
|
|
510
|
+
async function B(i, t = 1e4) {
|
|
511
|
+
const e = new AbortController(), r = setTimeout(() => e.abort(), t);
|
|
512
|
+
try {
|
|
513
|
+
const s = await fetch(i, { signal: e.signal });
|
|
514
|
+
if (!s.ok) return null;
|
|
515
|
+
const n = l.parseMetadata(await s.text());
|
|
516
|
+
return n ? l.extractAudioUrl(n) : null;
|
|
517
|
+
} catch {
|
|
518
|
+
return null;
|
|
519
|
+
} finally {
|
|
520
|
+
clearTimeout(r);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function G(i) {
|
|
524
|
+
if (i) {
|
|
525
|
+
if (typeof i == "string") return { text: i };
|
|
526
|
+
if (typeof i == "object" && !Array.isArray(i)) {
|
|
527
|
+
const t = i;
|
|
528
|
+
return { text: typeof t.text == "string" ? t.text : void 0 };
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
function b(i) {
|
|
533
|
+
if (!i || typeof i != "object" || Array.isArray(i)) return;
|
|
534
|
+
const t = i;
|
|
535
|
+
if (typeof t.uri == "string")
|
|
536
|
+
return {
|
|
537
|
+
uri: t.uri,
|
|
538
|
+
mime_type: typeof t.mime_type == "string" ? t.mime_type : void 0
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
function O(i) {
|
|
542
|
+
if (!i || typeof i != "object" || Array.isArray(i)) return;
|
|
543
|
+
const t = i;
|
|
544
|
+
if (typeof t.uri == "string")
|
|
545
|
+
return {
|
|
546
|
+
uri: t.uri,
|
|
547
|
+
mime_type: typeof t.mime_type == "string" ? t.mime_type : void 0,
|
|
548
|
+
type: typeof t.type == "string" ? t.type : void 0
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
function K(i) {
|
|
552
|
+
if (!Array.isArray(i)) return;
|
|
553
|
+
const t = [];
|
|
554
|
+
for (const e of i)
|
|
555
|
+
if (e && typeof e == "object") {
|
|
556
|
+
const r = e;
|
|
557
|
+
typeof r.name == "string" && typeof r.credit == "string" && t.push({ name: r.name, credit: r.credit });
|
|
558
|
+
}
|
|
559
|
+
return t.length > 0 ? t : void 0;
|
|
560
|
+
}
|
|
561
|
+
function V(i) {
|
|
562
|
+
const t = i.stems;
|
|
563
|
+
if (!Array.isArray(t)) return [];
|
|
564
|
+
const e = [];
|
|
565
|
+
for (const r of t)
|
|
566
|
+
if (r && typeof r == "object") {
|
|
567
|
+
const s = r;
|
|
568
|
+
e.push({
|
|
569
|
+
name: typeof s.name == "string" ? s.name : "",
|
|
570
|
+
uri: typeof s.uri == "string" ? s.uri : void 0,
|
|
571
|
+
mime_type: typeof s.mime_type == "string" ? s.mime_type : void 0
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
return e;
|
|
575
|
+
}
|
|
576
|
+
function Y(i) {
|
|
577
|
+
const t = i.mixes;
|
|
578
|
+
if (!Array.isArray(t)) return [];
|
|
579
|
+
const e = [];
|
|
580
|
+
for (const r of t)
|
|
581
|
+
if (r && typeof r == "object") {
|
|
582
|
+
const s = r, n = q(s.name), c = J(s.conditions);
|
|
583
|
+
e.push({
|
|
584
|
+
name: n || "",
|
|
585
|
+
uri: typeof s.uri == "string" ? s.uri : void 0,
|
|
586
|
+
mime_type: typeof s.mime_type == "string" ? s.mime_type : void 0,
|
|
587
|
+
conditions: c
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
return e;
|
|
591
|
+
}
|
|
592
|
+
function q(i) {
|
|
593
|
+
if (typeof i == "string") return i;
|
|
594
|
+
if (i && typeof i == "object" && !Array.isArray(i)) {
|
|
595
|
+
const t = i;
|
|
596
|
+
if (typeof t.value == "string") return t.value;
|
|
597
|
+
}
|
|
598
|
+
return "";
|
|
599
|
+
}
|
|
600
|
+
function J(i) {
|
|
601
|
+
if (!Array.isArray(i)) return [];
|
|
602
|
+
const t = [];
|
|
603
|
+
for (const e of i)
|
|
604
|
+
if (e && typeof e == "object") {
|
|
605
|
+
const r = e;
|
|
606
|
+
typeof r.type == "string" && (typeof r.value == "string" || typeof r.value == "number" || typeof r.value == "boolean") && t.push({ type: r.type, value: String(r.value) });
|
|
607
|
+
}
|
|
608
|
+
return t;
|
|
609
|
+
}
|
|
610
|
+
function Z(i, t) {
|
|
611
|
+
const e = [], r = /* @__PURE__ */ new Date();
|
|
612
|
+
for (const s of i.mixes || []) {
|
|
613
|
+
if (s.conditions.length === 0) {
|
|
614
|
+
e.push({
|
|
615
|
+
mix: s,
|
|
616
|
+
conditions: [],
|
|
617
|
+
allMet: !0,
|
|
618
|
+
unmetConditions: []
|
|
619
|
+
});
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
const n = [], c = [];
|
|
623
|
+
for (const a of s.conditions) {
|
|
624
|
+
const o = Q(a, t, r);
|
|
625
|
+
n.push(o), o.met || c.push(o);
|
|
626
|
+
}
|
|
627
|
+
e.push({
|
|
628
|
+
mix: s,
|
|
629
|
+
conditions: n,
|
|
630
|
+
allMet: c.length === 0,
|
|
631
|
+
unmetConditions: c
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
return e;
|
|
635
|
+
}
|
|
636
|
+
function Q(i, t, e = /* @__PURE__ */ new Date()) {
|
|
637
|
+
const { type: r, value: s } = i, n = t?.environment, c = t?.appState;
|
|
638
|
+
switch (r) {
|
|
639
|
+
case "weather": {
|
|
640
|
+
const a = n?.weather?.weatherType || "", o = a.toLowerCase() === s.toLowerCase();
|
|
641
|
+
return {
|
|
642
|
+
type: r,
|
|
643
|
+
value: s,
|
|
644
|
+
met: o,
|
|
645
|
+
reason: o ? `Weather is ${a}` : `Weather is ${a} (need ${s})`
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
case "day": {
|
|
649
|
+
const o = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][e.getDay()], u = o.toLowerCase() === s.toLowerCase();
|
|
650
|
+
return {
|
|
651
|
+
type: r,
|
|
652
|
+
value: s,
|
|
653
|
+
met: u,
|
|
654
|
+
reason: u ? `Today is ${o}` : `Today is ${o} (need ${s})`
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
case "start_time": {
|
|
658
|
+
const a = e.getHours() * 60 + e.getMinutes(), o = k(s), u = a >= o;
|
|
659
|
+
return {
|
|
660
|
+
type: r,
|
|
661
|
+
value: s,
|
|
662
|
+
met: u,
|
|
663
|
+
reason: u ? `Time is after ${s}` : `Time is before ${s}`
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
case "end_time": {
|
|
667
|
+
const a = e.getHours() * 60 + e.getMinutes(), o = k(s), u = a < o;
|
|
668
|
+
return {
|
|
669
|
+
type: r,
|
|
670
|
+
value: s,
|
|
671
|
+
met: u,
|
|
672
|
+
reason: u ? `Time is before ${s}` : `Time is after ${s}`
|
|
673
|
+
};
|
|
674
|
+
}
|
|
675
|
+
case "min_plays": {
|
|
676
|
+
const a = parseInt(s, 10), o = c?.playCount ?? 0, u = o >= a;
|
|
677
|
+
return {
|
|
678
|
+
type: r,
|
|
679
|
+
value: s,
|
|
680
|
+
met: u,
|
|
681
|
+
reason: u ? `Played ${o} times (need ${a})` : `Played ${o} times (need ${a})`
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
case "max_plays": {
|
|
685
|
+
const a = parseInt(s, 10), o = c?.playCount ?? 0, u = o <= a;
|
|
686
|
+
return {
|
|
687
|
+
type: r,
|
|
688
|
+
value: s,
|
|
689
|
+
met: u,
|
|
690
|
+
reason: u ? `Played ${o} times (max ${a})` : `Played ${o} times (exceeds max ${a})`
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
case "every_x_plays": {
|
|
694
|
+
const a = parseInt(s, 10), o = c?.playCount ?? 0, u = a > 0 && o > 0 && o % a === 0;
|
|
695
|
+
return {
|
|
696
|
+
type: r,
|
|
697
|
+
value: s,
|
|
698
|
+
met: u,
|
|
699
|
+
reason: u ? `Play #${o} (every ${a} plays)` : `Play #${o} (not a multiple of ${a})`
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
case "altitude": {
|
|
703
|
+
const a = n?.geolocation?.altitude;
|
|
704
|
+
if (a == null)
|
|
705
|
+
return { type: r, value: s, met: !1, reason: "Altitude unavailable" };
|
|
706
|
+
const o = X(s), u = tt(a, o.operator, o.threshold);
|
|
707
|
+
return {
|
|
708
|
+
type: r,
|
|
709
|
+
value: s,
|
|
710
|
+
met: u,
|
|
711
|
+
reason: u ? `Altitude ${a}m ${o.operator} ${o.threshold}m` : `Altitude ${a}m (need ${o.operator} ${o.threshold}m)`
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
case "favorite": {
|
|
715
|
+
const a = c?.isFavorite ?? !1, o = s.toLowerCase() === "true", u = a === o;
|
|
716
|
+
return {
|
|
717
|
+
type: r,
|
|
718
|
+
value: s,
|
|
719
|
+
met: u,
|
|
720
|
+
reason: u ? a ? "Track is favorited" : "Track is not favorited" : a ? "Track is favorited (need unfavorited)" : "Track is not favorited (need favorited)"
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
case "birthday": {
|
|
724
|
+
if (!c?.userBirthday)
|
|
725
|
+
return { type: r, value: s, met: !1, reason: "Birthday not set" };
|
|
726
|
+
const o = `${String(e.getMonth() + 1).padStart(2, "0")}-${String(e.getDate()).padStart(2, "0")}` === c.userBirthday;
|
|
727
|
+
return {
|
|
728
|
+
type: r,
|
|
729
|
+
value: s,
|
|
730
|
+
met: o,
|
|
731
|
+
reason: o ? "It's your birthday!" : `Not your birthday (need ${c.userBirthday})`
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
case "weight":
|
|
735
|
+
return {
|
|
736
|
+
type: r,
|
|
737
|
+
value: s,
|
|
738
|
+
met: !0,
|
|
739
|
+
reason: `Weight ${s} (used for random selection, always available)`
|
|
740
|
+
};
|
|
741
|
+
default:
|
|
742
|
+
return {
|
|
743
|
+
type: r,
|
|
744
|
+
value: s,
|
|
745
|
+
met: !0,
|
|
746
|
+
reason: `Custom condition: ${r} = ${s}`
|
|
747
|
+
};
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
function k(i) {
|
|
751
|
+
const t = i.split(":");
|
|
752
|
+
if (t.length !== 2) return 0;
|
|
753
|
+
const e = parseInt(t[0], 10), r = parseInt(t[1], 10);
|
|
754
|
+
return isNaN(e) || isNaN(r) ? 0 : e * 60 + r;
|
|
755
|
+
}
|
|
756
|
+
function X(i) {
|
|
757
|
+
const t = i.match(/^(>=|<=|>|<|=)?\s*(\d+(?:\.\d+)?)\s*$/);
|
|
758
|
+
return t ? {
|
|
759
|
+
operator: t[1] || ">=",
|
|
760
|
+
threshold: parseFloat(t[2])
|
|
761
|
+
} : { operator: ">=", threshold: parseFloat(i) || 0 };
|
|
762
|
+
}
|
|
763
|
+
function tt(i, t, e) {
|
|
764
|
+
switch (t) {
|
|
765
|
+
case ">":
|
|
766
|
+
return i > e;
|
|
767
|
+
case ">=":
|
|
768
|
+
return i >= e;
|
|
769
|
+
case "<":
|
|
770
|
+
return i < e;
|
|
771
|
+
case "<=":
|
|
772
|
+
return i <= e;
|
|
773
|
+
case "=":
|
|
774
|
+
case "==":
|
|
775
|
+
return i === e;
|
|
776
|
+
default:
|
|
777
|
+
return i >= e;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
function y(i) {
|
|
781
|
+
if ("audio_url" in i && typeof i.audio_url == "string")
|
|
782
|
+
return i.audio_url;
|
|
783
|
+
if ("metadata" in i) {
|
|
784
|
+
const t = l.parseMetadata(i.metadata);
|
|
785
|
+
if (t)
|
|
786
|
+
return l.extractAudioUrl(t);
|
|
787
|
+
}
|
|
788
|
+
return null;
|
|
789
|
+
}
|
|
790
|
+
function U(i) {
|
|
791
|
+
if ("audio_url_lossless" in i && typeof i.audio_url_lossless == "string")
|
|
792
|
+
return i.audio_url_lossless;
|
|
793
|
+
if ("metadata" in i) {
|
|
794
|
+
const t = l.parseMetadata(i.metadata);
|
|
795
|
+
if (t)
|
|
796
|
+
return l.extractAudioUrlLossless(t);
|
|
797
|
+
}
|
|
798
|
+
return null;
|
|
799
|
+
}
|
|
800
|
+
function _(i) {
|
|
801
|
+
if ("image_url" in i && typeof i.image_url == "string")
|
|
802
|
+
return i.image_url;
|
|
803
|
+
if ("metadata" in i) {
|
|
804
|
+
if ("artwork_url" in i && typeof i.artwork_url == "string")
|
|
805
|
+
return i.artwork_url;
|
|
806
|
+
const t = l.parseMetadata(i.metadata);
|
|
807
|
+
if (t)
|
|
808
|
+
return l.extractImageUrl(t);
|
|
809
|
+
}
|
|
810
|
+
return null;
|
|
811
|
+
}
|
|
812
|
+
function L(i) {
|
|
813
|
+
if ("image_thumb_url" in i && typeof i.image_thumb_url == "string")
|
|
814
|
+
return i.image_thumb_url;
|
|
815
|
+
if ("metadata" in i) {
|
|
816
|
+
const t = l.parseMetadata(i.metadata);
|
|
817
|
+
if (t)
|
|
818
|
+
return l.extractImageThumbUrl(t);
|
|
819
|
+
}
|
|
820
|
+
return null;
|
|
821
|
+
}
|
|
822
|
+
function C(i) {
|
|
823
|
+
if ("metadata" in i) {
|
|
824
|
+
const t = l.parseMetadata(i.metadata);
|
|
825
|
+
if (t && typeof t.vrm == "string" && t.vrm)
|
|
826
|
+
return t.vrm;
|
|
827
|
+
}
|
|
828
|
+
return "extras" in i && i.extras && typeof i.extras.vrm == "string" && i.extras.vrm || null;
|
|
829
|
+
}
|
|
830
|
+
function j(i) {
|
|
831
|
+
if ("extras" in i && i.extras?.mixes)
|
|
832
|
+
return i.extras.mixes;
|
|
833
|
+
if ("metadata" in i) {
|
|
834
|
+
const t = l.parseMetadata(i.metadata);
|
|
835
|
+
if (t)
|
|
836
|
+
return h(t).mixes ?? [];
|
|
837
|
+
}
|
|
838
|
+
return [];
|
|
839
|
+
}
|
|
840
|
+
function E(i) {
|
|
841
|
+
if ("audio_url" in i || !("metadata" in i))
|
|
842
|
+
return null;
|
|
843
|
+
const t = l.parseMetadata(i.metadata);
|
|
844
|
+
return M(
|
|
845
|
+
i.selected_mix,
|
|
846
|
+
l.convertAttributes(t?.attributes),
|
|
847
|
+
t ? h(t).mixes : void 0
|
|
848
|
+
);
|
|
849
|
+
}
|
|
850
|
+
function dt(i) {
|
|
851
|
+
const t = [];
|
|
852
|
+
for (const e of i.tracks) {
|
|
853
|
+
const r = y(e);
|
|
854
|
+
r && t.push(r);
|
|
855
|
+
}
|
|
856
|
+
return t;
|
|
857
|
+
}
|
|
858
|
+
function yt(i) {
|
|
859
|
+
const t = [];
|
|
860
|
+
for (const e of i.tracks) {
|
|
861
|
+
const r = _(e);
|
|
862
|
+
r && t.push(r);
|
|
863
|
+
}
|
|
864
|
+
return t;
|
|
865
|
+
}
|
|
866
|
+
function _t(i) {
|
|
867
|
+
const t = [];
|
|
868
|
+
for (const e of i.tracks) {
|
|
869
|
+
if ("title" in e && typeof e.title == "string") {
|
|
870
|
+
t.push(e.title);
|
|
871
|
+
continue;
|
|
872
|
+
}
|
|
873
|
+
if ("metadata" in e) {
|
|
874
|
+
const r = l.parseMetadata(e.metadata);
|
|
875
|
+
if (r) {
|
|
876
|
+
const s = l.extractTitle(r);
|
|
877
|
+
s && t.push(s);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
return t;
|
|
882
|
+
}
|
|
883
|
+
function gt(i) {
|
|
884
|
+
const t = [];
|
|
885
|
+
for (const e of i.tracks) {
|
|
886
|
+
if ("artist" in e && typeof e.artist == "string") {
|
|
887
|
+
t.push(e.artist);
|
|
888
|
+
continue;
|
|
889
|
+
}
|
|
890
|
+
if ("metadata" in e) {
|
|
891
|
+
const r = l.parseMetadata(e.metadata);
|
|
892
|
+
if (r) {
|
|
893
|
+
const s = l.extractArtist(r);
|
|
894
|
+
s && t.push(s);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
return t;
|
|
899
|
+
}
|
|
900
|
+
function ht(i) {
|
|
901
|
+
const t = /* @__PURE__ */ new Set();
|
|
902
|
+
for (const e of i.tracks) {
|
|
903
|
+
if ("genre" in e && typeof e.genre == "string" && e.genre) {
|
|
904
|
+
t.add(e.genre);
|
|
905
|
+
continue;
|
|
906
|
+
}
|
|
907
|
+
if ("metadata" in e) {
|
|
908
|
+
const r = l.parseMetadata(e.metadata);
|
|
909
|
+
r && typeof r.genre == "string" && r.genre && t.add(r.genre);
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
return Array.from(t).sort();
|
|
913
|
+
}
|
|
914
|
+
function vt(i) {
|
|
915
|
+
const t = /* @__PURE__ */ new Set();
|
|
916
|
+
for (const e of i.tracks) {
|
|
917
|
+
if ("tags" in e && Array.isArray(e.tags)) {
|
|
918
|
+
for (const r of e.tags)
|
|
919
|
+
r && t.add(r.toLowerCase());
|
|
920
|
+
continue;
|
|
921
|
+
}
|
|
922
|
+
if ("metadata" in e) {
|
|
923
|
+
const r = l.parseMetadata(e.metadata);
|
|
924
|
+
if (r && Array.isArray(r.tags))
|
|
925
|
+
for (const s of r.tags)
|
|
926
|
+
s && t.add(String(s).toLowerCase());
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
return Array.from(t).sort();
|
|
930
|
+
}
|
|
931
|
+
function At(i) {
|
|
932
|
+
let t = 0;
|
|
933
|
+
for (const e of i.tracks) {
|
|
934
|
+
if ("duration" in e && typeof e.duration == "number") {
|
|
935
|
+
t += e.duration;
|
|
936
|
+
continue;
|
|
937
|
+
}
|
|
938
|
+
if ("metadata" in e) {
|
|
939
|
+
const r = l.parseMetadata(e.metadata);
|
|
940
|
+
r && typeof r.duration == "number" && (t += r.duration);
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
return t;
|
|
944
|
+
}
|
|
945
|
+
function xt(i) {
|
|
946
|
+
return i.tracks.length;
|
|
947
|
+
}
|
|
948
|
+
function bt(i) {
|
|
949
|
+
const t = [];
|
|
950
|
+
for (const e of i.tracks) {
|
|
951
|
+
let r = y(e);
|
|
952
|
+
const s = _(e), n = L(e), c = E(e);
|
|
953
|
+
c?.audio_url && (r = c.audio_url);
|
|
954
|
+
const a = c ? c.name : "audio_url" in e ? e.selected_mix : void 0;
|
|
955
|
+
let o = "", u = "", p;
|
|
956
|
+
if ("title" in e)
|
|
957
|
+
o = e.title || "", u = e.artist || "", p = e.description || void 0;
|
|
958
|
+
else if ("metadata" in e) {
|
|
959
|
+
const f = l.parseMetadata(e.metadata);
|
|
960
|
+
f && (o = l.extractTitle(f) || "", u = l.extractArtist(f) || "", p = l.extractDescription(f) || void 0);
|
|
961
|
+
}
|
|
962
|
+
if (r) {
|
|
963
|
+
const f = {
|
|
964
|
+
title: o,
|
|
965
|
+
artist: u,
|
|
966
|
+
audio_url: r,
|
|
967
|
+
image_url: s || ""
|
|
968
|
+
}, m = U(e), d = c ? c.audio_url_lossless : m && m !== r ? m : void 0;
|
|
969
|
+
d && (f.audio_url_lossless = d), a && (f.selected_mix = a), n && (f.image_thumb_url = n), p && (f.description = p), "audio_ipfs_hash" in e && typeof e.audio_ipfs_hash == "string" && (f.audio_ipfs_hash = e.audio_ipfs_hash), "artwork_ipfs_hash" in e && typeof e.artwork_ipfs_hash == "string" && (f.artwork_ipfs_hash = e.artwork_ipfs_hash), "mint_function" in e && typeof e.mint_function == "string" && (f.mint_function = e.mint_function), "mint_price" in e && typeof e.mint_price == "string" && (f.mint_price = e.mint_price), "mint_snapshot_time" in e && typeof e.mint_snapshot_time == "number" && (f.mint_snapshot_time = e.mint_snapshot_time), "mint_token" in e && typeof e.mint_token == "string" && (f.mint_token = e.mint_token), t.push(f);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
return t;
|
|
973
|
+
}
|
|
974
|
+
function kt(i) {
|
|
975
|
+
const t = [];
|
|
976
|
+
for (const e of i.tracks) {
|
|
977
|
+
if ("audio_url" in e) {
|
|
978
|
+
t.push({ ...e });
|
|
979
|
+
continue;
|
|
980
|
+
}
|
|
981
|
+
if ("metadata" in e) {
|
|
982
|
+
const r = l.parseMetadata(e.metadata);
|
|
983
|
+
if (r) {
|
|
984
|
+
const s = h(r), n = M(
|
|
985
|
+
e.selected_mix,
|
|
986
|
+
l.convertAttributes(r.attributes),
|
|
987
|
+
s.mixes
|
|
988
|
+
), c = l.extractAudioUrlLossless(r), a = l.extractAudioUrl(r), o = n?.audio_url || a, u = n ? n.audio_url_lossless : c && c !== o ? c : void 0;
|
|
989
|
+
t.push({
|
|
990
|
+
id: e.chain_name === "AR" ? `AR-${e.tx_id}` : `${e.chain_name}-${e.token_address}-${e.token_id}`,
|
|
991
|
+
chain_name: e.chain_name,
|
|
992
|
+
token_address: e.token_address,
|
|
993
|
+
token_id: e.token_id,
|
|
994
|
+
tx_id: e.tx_id,
|
|
995
|
+
platform: e.platform,
|
|
996
|
+
title: l.extractTitle(r),
|
|
997
|
+
artist: l.extractArtist(r),
|
|
998
|
+
audio_url: o,
|
|
999
|
+
image_url: l.extractImageUrl(r),
|
|
1000
|
+
image_thumb_url: l.extractImageThumbUrl(r),
|
|
1001
|
+
duration: r.duration,
|
|
1002
|
+
genre: r.genre,
|
|
1003
|
+
tags: r.tags,
|
|
1004
|
+
bpm: r.bpm,
|
|
1005
|
+
key: r.key,
|
|
1006
|
+
album: r.album,
|
|
1007
|
+
description: l.extractDescription(r),
|
|
1008
|
+
attributes: l.convertAttributes(r.attributes),
|
|
1009
|
+
...u ? { audio_url_lossless: u } : {},
|
|
1010
|
+
...n ? { selected_mix: n.name } : {},
|
|
1011
|
+
...s.hasExtras ? { extras: s } : {},
|
|
1012
|
+
// v0.4 IPFS hash + mint fields from the raw track wrapper
|
|
1013
|
+
...e.audio_ipfs_hash ? { audio_ipfs_hash: e.audio_ipfs_hash } : {},
|
|
1014
|
+
...e.artwork_ipfs_hash ? { artwork_ipfs_hash: e.artwork_ipfs_hash } : {},
|
|
1015
|
+
...e.mint_function ? { mint_function: e.mint_function } : {},
|
|
1016
|
+
...e.mint_price ? { mint_price: e.mint_price } : {},
|
|
1017
|
+
...e.mint_snapshot_time ? { mint_snapshot_time: e.mint_snapshot_time } : {},
|
|
1018
|
+
...e.mint_token ? { mint_token: e.mint_token } : {}
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
return t;
|
|
1024
|
+
}
|
|
1025
|
+
function wt(i) {
|
|
1026
|
+
const t = [];
|
|
1027
|
+
for (const e of i.tracks) {
|
|
1028
|
+
const r = C(e);
|
|
1029
|
+
r && t.push(r);
|
|
1030
|
+
}
|
|
1031
|
+
return t;
|
|
1032
|
+
}
|
|
1033
|
+
function Mt(i) {
|
|
1034
|
+
const t = [];
|
|
1035
|
+
for (const e of i.tracks) {
|
|
1036
|
+
const r = C(e);
|
|
1037
|
+
if (!r) continue;
|
|
1038
|
+
const s = y(e), n = _(e), c = L(e);
|
|
1039
|
+
let a = "", o = "", u;
|
|
1040
|
+
if ("title" in e)
|
|
1041
|
+
a = e.title || "", o = e.artist || "", u = e.description || void 0;
|
|
1042
|
+
else if ("metadata" in e) {
|
|
1043
|
+
const f = l.parseMetadata(e.metadata);
|
|
1044
|
+
f && (a = l.extractTitle(f) || "", o = l.extractArtist(f) || "", u = l.extractDescription(f) || void 0);
|
|
1045
|
+
}
|
|
1046
|
+
const p = {
|
|
1047
|
+
title: a,
|
|
1048
|
+
artist: o,
|
|
1049
|
+
audio_url: s || "",
|
|
1050
|
+
image_url: n || "",
|
|
1051
|
+
vrm: r
|
|
1052
|
+
};
|
|
1053
|
+
c && (p.image_thumb_url = c), u && (p.description = u), t.push(p);
|
|
1054
|
+
}
|
|
1055
|
+
return t;
|
|
1056
|
+
}
|
|
1057
|
+
function Tt(i) {
|
|
1058
|
+
const t = [];
|
|
1059
|
+
for (const e of i.tracks)
|
|
1060
|
+
t.push(...j(e));
|
|
1061
|
+
return t;
|
|
1062
|
+
}
|
|
1063
|
+
function $t(i) {
|
|
1064
|
+
const t = [];
|
|
1065
|
+
for (const e of i.tracks) {
|
|
1066
|
+
const r = j(e);
|
|
1067
|
+
if (r.length === 0) continue;
|
|
1068
|
+
const s = E(e);
|
|
1069
|
+
let n = "", c = "", a;
|
|
1070
|
+
if ("title" in e)
|
|
1071
|
+
n = e.title || "", c = e.artist || "", a = e.description || void 0;
|
|
1072
|
+
else if ("metadata" in e) {
|
|
1073
|
+
const f = l.parseMetadata(e.metadata);
|
|
1074
|
+
f && (n = l.extractTitle(f) || "", c = l.extractArtist(f) || "", a = l.extractDescription(f) || void 0);
|
|
1075
|
+
}
|
|
1076
|
+
let o = y(e) || "", u;
|
|
1077
|
+
if (s)
|
|
1078
|
+
o = s.audio_url || o, u = s.audio_url_lossless;
|
|
1079
|
+
else {
|
|
1080
|
+
const f = U(e);
|
|
1081
|
+
u = f && f !== o ? f : void 0;
|
|
1082
|
+
}
|
|
1083
|
+
const p = s ? s.name : "audio_url" in e ? e.selected_mix : void 0;
|
|
1084
|
+
t.push({
|
|
1085
|
+
title: n,
|
|
1086
|
+
artist: c,
|
|
1087
|
+
audio_url: o,
|
|
1088
|
+
image_url: _(e) || "",
|
|
1089
|
+
...u ? { audio_url_lossless: u } : {},
|
|
1090
|
+
...p ? { selected_mix: p } : {},
|
|
1091
|
+
...a ? { description: a } : {},
|
|
1092
|
+
mixes: r
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
return t;
|
|
1096
|
+
}
|
|
1097
|
+
const v = [
|
|
1098
|
+
"ipfs.io",
|
|
1099
|
+
"cloudflare-ipfs.com",
|
|
1100
|
+
"dweb.link",
|
|
1101
|
+
"gateway.pinata.cloud",
|
|
1102
|
+
"ipfs.infura.io",
|
|
1103
|
+
"nftstorage.link",
|
|
1104
|
+
"web3-music-pipeline.mypinata.cloud",
|
|
1105
|
+
"soundxyz.mypinata.cloud",
|
|
1106
|
+
"gold-broad-gibbon-29.mypinata.cloud",
|
|
1107
|
+
"bonfire.mypinata.cloud",
|
|
1108
|
+
"spinamp.mypinata.cloud",
|
|
1109
|
+
"catalogworks.b-cdn.net",
|
|
1110
|
+
"www.heds.cloud"
|
|
1111
|
+
], et = "ipfs.io", g = "/ipfs/", rt = /^Qm[1-9A-HJ-NP-Za-km-z]{44}$/, it = /^b[a-z2-7]{55,}$/;
|
|
1112
|
+
function st(i) {
|
|
1113
|
+
return rt.test(i) || it.test(i);
|
|
1114
|
+
}
|
|
1115
|
+
const nt = /^ipfs_[a-z]+$/;
|
|
1116
|
+
function I(i) {
|
|
1117
|
+
const t = i.split("/").filter(Boolean);
|
|
1118
|
+
for (let e = 0; e < t.length - 1; e++)
|
|
1119
|
+
if (nt.test(t[e]) && st(t[e + 1])) {
|
|
1120
|
+
const r = t[e + 1], s = t.slice(e + 2);
|
|
1121
|
+
return s.length > 0 ? `${r}/${s.join("/")}` : r;
|
|
1122
|
+
}
|
|
1123
|
+
return null;
|
|
1124
|
+
}
|
|
1125
|
+
function ot(i) {
|
|
1126
|
+
if (!i || typeof i != "string") return null;
|
|
1127
|
+
if (i.startsWith("ipfs://ipfs/"))
|
|
1128
|
+
return i.slice(12);
|
|
1129
|
+
if (i.startsWith("ipfs://"))
|
|
1130
|
+
return i.slice(7);
|
|
1131
|
+
try {
|
|
1132
|
+
const t = new URL(i), { hostname: e, pathname: r } = t, s = e.match(/^([A-Za-z0-9]+)\.ipfs\.dweb\.link$/);
|
|
1133
|
+
if (s)
|
|
1134
|
+
return `${s[1]}${r}`;
|
|
1135
|
+
if (v.some(
|
|
1136
|
+
(a) => e === a || e.endsWith("." + a)
|
|
1137
|
+
) && r.startsWith(g))
|
|
1138
|
+
return r.slice(g.length);
|
|
1139
|
+
const c = I(r);
|
|
1140
|
+
if (c) return c;
|
|
1141
|
+
} catch {
|
|
1142
|
+
}
|
|
1143
|
+
return null;
|
|
1144
|
+
}
|
|
1145
|
+
function St(i) {
|
|
1146
|
+
if (!i || typeof i != "string") return !1;
|
|
1147
|
+
if (i.startsWith("ipfs://")) return !0;
|
|
1148
|
+
try {
|
|
1149
|
+
const t = new URL(i), { hostname: e, pathname: r } = t;
|
|
1150
|
+
if (/^[A-Za-z0-9]+\.ipfs\.dweb\.link$/.test(e) || v.some(
|
|
1151
|
+
(n) => e === n || e.endsWith("." + n)
|
|
1152
|
+
) && r.startsWith(g) || I(r)) return !0;
|
|
1153
|
+
} catch {
|
|
1154
|
+
}
|
|
1155
|
+
return !1;
|
|
1156
|
+
}
|
|
1157
|
+
function at(i) {
|
|
1158
|
+
if (!(!i || i.startsWith("ipfs://")))
|
|
1159
|
+
try {
|
|
1160
|
+
const { hostname: t } = new URL(i);
|
|
1161
|
+
return v.some(
|
|
1162
|
+
(r) => t === r || t.endsWith("." + r)
|
|
1163
|
+
) ? t : void 0;
|
|
1164
|
+
} catch {
|
|
1165
|
+
return;
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
function Ut(i, t = {}) {
|
|
1169
|
+
if (!i) return i;
|
|
1170
|
+
const e = typeof t == "string" ? { gatewayHost: t } : t, { gatewayHost: r = et, preserveOriginalGateway: s = !1 } = e, n = ot(i);
|
|
1171
|
+
if (n) {
|
|
1172
|
+
if (s) {
|
|
1173
|
+
const c = at(i);
|
|
1174
|
+
if (c)
|
|
1175
|
+
return `https://${c}/ipfs/${n}`;
|
|
1176
|
+
}
|
|
1177
|
+
return `https://${r}/ipfs/${n}`;
|
|
1178
|
+
}
|
|
1179
|
+
return i;
|
|
1180
|
+
}
|
|
1181
|
+
export {
|
|
1182
|
+
et as D,
|
|
1183
|
+
v as K,
|
|
1184
|
+
l as M,
|
|
1185
|
+
ot as a,
|
|
1186
|
+
gt as b,
|
|
1187
|
+
dt as c,
|
|
1188
|
+
kt as d,
|
|
1189
|
+
Z as e,
|
|
1190
|
+
ft as f,
|
|
1191
|
+
h as g,
|
|
1192
|
+
ht as h,
|
|
1193
|
+
yt as i,
|
|
1194
|
+
$t as j,
|
|
1195
|
+
Tt as k,
|
|
1196
|
+
T as l,
|
|
1197
|
+
vt as m,
|
|
1198
|
+
At as n,
|
|
1199
|
+
xt as o,
|
|
1200
|
+
lt as p,
|
|
1201
|
+
_t as q,
|
|
1202
|
+
M as r,
|
|
1203
|
+
bt as s,
|
|
1204
|
+
pt as t,
|
|
1205
|
+
Mt as u,
|
|
1206
|
+
wt as v,
|
|
1207
|
+
St as w,
|
|
1208
|
+
Ut as x,
|
|
1209
|
+
A as y,
|
|
1210
|
+
mt as z
|
|
1211
|
+
};
|