fluncle 0.139.0 → 0.140.0
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/bin/fluncle.mjs +115 -9
- package/package.json +1 -1
package/bin/fluncle.mjs
CHANGED
|
@@ -561,7 +561,7 @@ function parseVersion(version) {
|
|
|
561
561
|
var currentVersion;
|
|
562
562
|
var init_version = __esm(() => {
|
|
563
563
|
init_output();
|
|
564
|
-
currentVersion = "0.
|
|
564
|
+
currentVersion = "0.140.0".trim() ? "0.140.0".trim() : "0.1.0";
|
|
565
565
|
});
|
|
566
566
|
|
|
567
567
|
// src/update-notifier.ts
|
|
@@ -2189,7 +2189,7 @@ function parseVersion2(version) {
|
|
|
2189
2189
|
var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
|
|
2190
2190
|
var init_version2 = __esm(() => {
|
|
2191
2191
|
init_output();
|
|
2192
|
-
currentVersion2 = "0.
|
|
2192
|
+
currentVersion2 = "0.140.0".trim() ? "0.140.0".trim() : "0.1.0";
|
|
2193
2193
|
});
|
|
2194
2194
|
|
|
2195
2195
|
// ../../packages/registry/src/index.ts
|
|
@@ -3666,7 +3666,8 @@ __export(exports_admin_tracks, {
|
|
|
3666
3666
|
captureQueueCommand: () => captureQueueCommand,
|
|
3667
3667
|
backfillLastfmCommand: () => backfillLastfmCommand,
|
|
3668
3668
|
backfillDiscogsCommand: () => backfillDiscogsCommand,
|
|
3669
|
-
backfillAppleMusicCommand: () => backfillAppleMusicCommand
|
|
3669
|
+
backfillAppleMusicCommand: () => backfillAppleMusicCommand,
|
|
3670
|
+
backfillAppleCatalogueCommand: () => backfillAppleCatalogueCommand
|
|
3670
3671
|
});
|
|
3671
3672
|
async function fetchAdminTracks(options) {
|
|
3672
3673
|
const {
|
|
@@ -3846,6 +3847,10 @@ async function backfillAppleMusicCommand(limit, dryRun, cursor) {
|
|
|
3846
3847
|
}
|
|
3847
3848
|
return adminApiPost(`/api/admin/backfill/apple-music?${params.toString()}`);
|
|
3848
3849
|
}
|
|
3850
|
+
async function backfillAppleCatalogueCommand(limit, dryRun) {
|
|
3851
|
+
const params = new URLSearchParams({ dryRun: String(dryRun), limit: String(limit) });
|
|
3852
|
+
return adminApiPost(`/api/admin/backfill/apple-catalogue?${params.toString()}`);
|
|
3853
|
+
}
|
|
3849
3854
|
async function vehiclesCommand(limit) {
|
|
3850
3855
|
const tracks = await fetchAdminTracks({ hasVideo: true, max: limit, order: "desc" });
|
|
3851
3856
|
return tracks.map((track) => ({
|
|
@@ -5699,6 +5704,7 @@ var exports_admin_catalogue = {};
|
|
|
5699
5704
|
__export(exports_admin_catalogue, {
|
|
5700
5705
|
crawlStatusCommand: () => crawlStatusCommand,
|
|
5701
5706
|
crawlCatalogueCommand: () => crawlCatalogueCommand,
|
|
5707
|
+
clearWrongAudioCommand: () => clearWrongAudioCommand,
|
|
5702
5708
|
catalogueRankCommand: () => catalogueRankCommand,
|
|
5703
5709
|
catalogueListCommand: () => catalogueListCommand
|
|
5704
5710
|
});
|
|
@@ -5709,12 +5715,17 @@ async function catalogueRankCommand(options) {
|
|
|
5709
5715
|
}
|
|
5710
5716
|
async function catalogueListCommand(options) {
|
|
5711
5717
|
const params = new URLSearchParams;
|
|
5712
|
-
params.set("lens", options.lens === "capture" ? "capture" : "ear");
|
|
5718
|
+
params.set("lens", options.lens === "capture" ? "capture" : options.lens === "quarantine" ? "quarantine" : "ear");
|
|
5713
5719
|
if (options.limit) {
|
|
5714
5720
|
params.set("limit", options.limit);
|
|
5715
5721
|
}
|
|
5716
5722
|
return adminApiGet(`/api/admin/catalogue?${params.toString()}`);
|
|
5717
5723
|
}
|
|
5724
|
+
async function clearWrongAudioCommand(trackId) {
|
|
5725
|
+
return adminApiPost("/api/admin/catalogue/wrong-audio/clear", {
|
|
5726
|
+
trackId
|
|
5727
|
+
});
|
|
5728
|
+
}
|
|
5718
5729
|
async function crawlCatalogueCommand(limit, maxHop, dryRun) {
|
|
5719
5730
|
const params = new URLSearchParams({ limit: String(limit), maxHop: String(maxHop) });
|
|
5720
5731
|
if (dryRun) {
|
|
@@ -8761,10 +8772,19 @@ function addAdminCommands(program2) {
|
|
|
8761
8772
|
const { catalogueRankCommand: catalogueRankCommand2 } = await Promise.resolve().then(() => (init_admin_catalogue(), exports_admin_catalogue));
|
|
8762
8773
|
await runCatalogueRank(options, catalogueRankCommand2);
|
|
8763
8774
|
});
|
|
8764
|
-
catalogue.command("list").description("The ranked catalogue: closest to a finding (ear),
|
|
8775
|
+
catalogue.command("list").description("The ranked catalogue: closest to a finding (ear), next to capture, or wrong audio").option("--lens <lens>", "ear (default), capture, or quarantine", "ear").option("--limit <limit>", "Rows (default 50, max 200)").option("--json", "Print JSON", false).action(async (options) => {
|
|
8765
8776
|
const { catalogueListCommand: catalogueListCommand2 } = await Promise.resolve().then(() => (init_admin_catalogue(), exports_admin_catalogue));
|
|
8766
8777
|
await runCatalogueList(options, catalogueListCommand2);
|
|
8767
8778
|
});
|
|
8779
|
+
catalogue.command("clear-wrong-audio").description("Overrule the wrong-audio quarantine on one catalogue row (operator)").argument("<trackId>", "The catalogue track id to keep").option("--json", "Print JSON", false).action(async (trackId, options) => {
|
|
8780
|
+
const { clearWrongAudioCommand: clearWrongAudioCommand2 } = await Promise.resolve().then(() => (init_admin_catalogue(), exports_admin_catalogue));
|
|
8781
|
+
const { cleared } = await clearWrongAudioCommand2(trackId);
|
|
8782
|
+
if (options.json) {
|
|
8783
|
+
printJson({ cleared, ok: true });
|
|
8784
|
+
return;
|
|
8785
|
+
}
|
|
8786
|
+
console.log(cleared ? `Kept ${trackId}. Its audio re-embeds and rejoins the ranking on the next sweep.` : `${trackId} was not quarantined \u2014 nothing to clear.`);
|
|
8787
|
+
});
|
|
8768
8788
|
catalogue.command("crawl").description("Run one bounded, resumable pass of the catalogue crawler").option("--dry-run", "Report the seed plan and write nothing at all", false).option("--limit <limit>", "Frontier nodes to expand this pass", "10").option("--max-hop <maxHop>", "Graph distance from a seed label (0-3)", "2").option("--json", "Print JSON", false).action(async (options) => {
|
|
8769
8789
|
const { crawlCatalogueCommand: crawlCatalogueCommand2 } = await Promise.resolve().then(() => (init_admin_catalogue(), exports_admin_catalogue));
|
|
8770
8790
|
await runCrawlCatalogue(options, crawlCatalogueCommand2);
|
|
@@ -8793,6 +8813,10 @@ function addAdminCommands(program2) {
|
|
|
8793
8813
|
const { backfillAppleMusicCommand: backfillAppleMusicCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
8794
8814
|
await runBackfillAppleMusic(options, backfillAppleMusicCommand2);
|
|
8795
8815
|
});
|
|
8816
|
+
backfill.command("apple-catalogue").description("Resolve Apple URLs + album facts for catalogue tracks (batched exact ISRC)").option("--dry-run", "Report the eligible set without resolving or writing", false).option("--limit <limit>", "Max catalogue rows to resolve", "50").option("--json", "Print JSON", false).action(async (options) => {
|
|
8817
|
+
const { backfillAppleCatalogueCommand: backfillAppleCatalogueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
8818
|
+
await runBackfillAppleCatalogue(options, backfillAppleCatalogueCommand2);
|
|
8819
|
+
});
|
|
8796
8820
|
backfill.command("artists").description("Back-fill the artist entity (artists + track_artists) for existing findings").option("--dry-run", "Report which findings would be upserted without touching the DB", false).option("--limit <limit>", "Max findings to process", "50").option("--json", "Print JSON", false).action(async (options) => {
|
|
8797
8821
|
const { backfillArtistsCommand: backfillArtistsCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
|
|
8798
8822
|
await runBackfillArtists(options, backfillArtistsCommand2);
|
|
@@ -9213,23 +9237,30 @@ async function runBackfillAppleMusic(options, backfillAppleMusicCommand2) {
|
|
|
9213
9237
|
let cursor;
|
|
9214
9238
|
let dryRun = options.dryRun;
|
|
9215
9239
|
let throttled = false;
|
|
9240
|
+
let breakerTripped = false;
|
|
9216
9241
|
let configured = true;
|
|
9242
|
+
let albumFactsWritten = 0;
|
|
9217
9243
|
while (resolved.length + unresolved.length + failed.length < limit) {
|
|
9218
9244
|
const remaining = limit - (resolved.length + unresolved.length + failed.length);
|
|
9219
9245
|
const result = await backfillAppleMusicCommand2(remaining, options.dryRun, cursor);
|
|
9220
9246
|
dryRun = result.dryRun;
|
|
9221
9247
|
configured = result.configured;
|
|
9248
|
+
albumFactsWritten += result.albumFactsWritten;
|
|
9222
9249
|
resolved.push(...result.resolved);
|
|
9223
9250
|
unresolved.push(...result.unresolved);
|
|
9224
9251
|
failed.push(...result.failed);
|
|
9225
9252
|
skipped.push(...result.skipped);
|
|
9226
9253
|
if (!options.json) {
|
|
9227
9254
|
const verb2 = result.dryRun ? "would resolve" : "resolved";
|
|
9228
|
-
console.log(` \u2026${verb2} ${result.resolvedCount}; ${result.unresolvedCount} unresolved; ${result.failedCount} failed; ${result.skippedCount} skipped`);
|
|
9255
|
+
console.log(` \u2026${verb2} ${result.resolvedCount}; ${result.unresolvedCount} unresolved; ${result.failedCount} failed; ${result.skippedCount} skipped; ${result.albumFactsWritten} album facts`);
|
|
9229
9256
|
}
|
|
9230
9257
|
if (!result.configured) {
|
|
9231
9258
|
break;
|
|
9232
9259
|
}
|
|
9260
|
+
if (result.breakerTripped) {
|
|
9261
|
+
breakerTripped = true;
|
|
9262
|
+
break;
|
|
9263
|
+
}
|
|
9233
9264
|
if (result.rateLimited) {
|
|
9234
9265
|
throttled = true;
|
|
9235
9266
|
break;
|
|
@@ -9241,6 +9272,8 @@ async function runBackfillAppleMusic(options, backfillAppleMusicCommand2) {
|
|
|
9241
9272
|
}
|
|
9242
9273
|
if (options.json) {
|
|
9243
9274
|
printSweepJson({
|
|
9275
|
+
albumFactsWritten,
|
|
9276
|
+
breakerTripped,
|
|
9244
9277
|
configured,
|
|
9245
9278
|
dryRun,
|
|
9246
9279
|
failed,
|
|
@@ -9259,7 +9292,7 @@ async function runBackfillAppleMusic(options, backfillAppleMusicCommand2) {
|
|
|
9259
9292
|
return;
|
|
9260
9293
|
}
|
|
9261
9294
|
const verb = dryRun ? "Would resolve" : "Resolved";
|
|
9262
|
-
console.log(`${verb} ${resolved.length} Apple Music URL(s); ${unresolved.length} unresolved; ${failed.length} failed; ${skipped.length} skipped.`);
|
|
9295
|
+
console.log(`${verb} ${resolved.length} Apple Music URL(s); ${unresolved.length} unresolved; ${failed.length} failed; ${skipped.length} skipped; ${albumFactsWritten} album facts.`);
|
|
9263
9296
|
for (const item of resolved) {
|
|
9264
9297
|
console.log(` ${item.logId}: ${item.url}`);
|
|
9265
9298
|
}
|
|
@@ -9270,6 +9303,72 @@ async function runBackfillAppleMusic(options, backfillAppleMusicCommand2) {
|
|
|
9270
9303
|
process.exitCode = 1;
|
|
9271
9304
|
}
|
|
9272
9305
|
}
|
|
9306
|
+
async function runBackfillAppleCatalogue(options, backfillAppleCatalogueCommand2) {
|
|
9307
|
+
const limit = parseListLimit(options.limit);
|
|
9308
|
+
const resolved = [];
|
|
9309
|
+
const unresolved = [];
|
|
9310
|
+
const failed = [];
|
|
9311
|
+
let dryRun = options.dryRun;
|
|
9312
|
+
let throttled = false;
|
|
9313
|
+
let breakerTripped = false;
|
|
9314
|
+
let configured = true;
|
|
9315
|
+
let albumFactsWritten = 0;
|
|
9316
|
+
while (resolved.length + unresolved.length + failed.length < limit) {
|
|
9317
|
+
const remaining = limit - (resolved.length + unresolved.length + failed.length);
|
|
9318
|
+
const result = await backfillAppleCatalogueCommand2(remaining, options.dryRun);
|
|
9319
|
+
dryRun = result.dryRun;
|
|
9320
|
+
configured = result.configured;
|
|
9321
|
+
albumFactsWritten += result.albumFactsWritten;
|
|
9322
|
+
resolved.push(...result.resolved);
|
|
9323
|
+
unresolved.push(...result.unresolved);
|
|
9324
|
+
failed.push(...result.failed);
|
|
9325
|
+
if (!options.json) {
|
|
9326
|
+
const verb2 = result.dryRun ? "would resolve" : "resolved";
|
|
9327
|
+
console.log(` \u2026${verb2} ${result.resolvedCount}; ${result.unresolvedCount} unresolved; ${result.failedCount} failed; ${result.albumFactsWritten} album facts`);
|
|
9328
|
+
}
|
|
9329
|
+
if (!result.configured) {
|
|
9330
|
+
break;
|
|
9331
|
+
}
|
|
9332
|
+
if (result.breakerTripped) {
|
|
9333
|
+
breakerTripped = true;
|
|
9334
|
+
break;
|
|
9335
|
+
}
|
|
9336
|
+
if (result.rateLimited) {
|
|
9337
|
+
throttled = true;
|
|
9338
|
+
break;
|
|
9339
|
+
}
|
|
9340
|
+
if (result.resolvedCount === 0 && result.unresolvedCount === 0 && result.failedCount === 0) {
|
|
9341
|
+
break;
|
|
9342
|
+
}
|
|
9343
|
+
}
|
|
9344
|
+
if (options.json) {
|
|
9345
|
+
printSweepJson({
|
|
9346
|
+
albumFactsWritten,
|
|
9347
|
+
breakerTripped,
|
|
9348
|
+
configured,
|
|
9349
|
+
dryRun,
|
|
9350
|
+
failed,
|
|
9351
|
+
rateLimited: throttled,
|
|
9352
|
+
resolved,
|
|
9353
|
+
resolvedCount: resolved.length,
|
|
9354
|
+
unresolved,
|
|
9355
|
+
unresolvedCount: unresolved.length
|
|
9356
|
+
}, failed.length);
|
|
9357
|
+
return;
|
|
9358
|
+
}
|
|
9359
|
+
if (!configured) {
|
|
9360
|
+
console.log("Apple catalogue backfill is not configured (the Worker's MusicKit secrets are unset) \u2014 nothing resolved.");
|
|
9361
|
+
return;
|
|
9362
|
+
}
|
|
9363
|
+
const verb = dryRun ? "Would resolve" : "Resolved";
|
|
9364
|
+
console.log(`${verb} ${resolved.length} catalogue Apple URL(s); ${unresolved.length} unresolved; ${failed.length} failed; ${albumFactsWritten} album facts.`);
|
|
9365
|
+
for (const item of failed) {
|
|
9366
|
+
console.log(` ${item.trackId}: ${item.error}`);
|
|
9367
|
+
}
|
|
9368
|
+
if (failed.length > 0) {
|
|
9369
|
+
process.exitCode = 1;
|
|
9370
|
+
}
|
|
9371
|
+
}
|
|
9273
9372
|
async function runBackfillArtists(options, backfillArtistsCommand2) {
|
|
9274
9373
|
const limit = parseListLimit(options.limit);
|
|
9275
9374
|
const upserted = [];
|
|
@@ -10280,7 +10379,8 @@ async function runCatalogueRank(options, catalogueRankCommand2) {
|
|
|
10280
10379
|
printJson({ ok: true, summary });
|
|
10281
10380
|
return;
|
|
10282
10381
|
}
|
|
10283
|
-
|
|
10382
|
+
const quarantine = summary.quarantined > 0 ? ` Quarantined ${summary.quarantined} as wrong audio.` : "";
|
|
10383
|
+
console.log(`Ranked ${summary.scored} against ${summary.embeddedFindings} embedded findings; prioritized ${summary.prioritized} for capture.${quarantine} ${summary.remaining} still stale.`);
|
|
10284
10384
|
}
|
|
10285
10385
|
async function runCatalogueList(options, catalogueListCommand2) {
|
|
10286
10386
|
const { summary, tracks } = await catalogueListCommand2({
|
|
@@ -10292,11 +10392,17 @@ async function runCatalogueList(options, catalogueListCommand2) {
|
|
|
10292
10392
|
return;
|
|
10293
10393
|
}
|
|
10294
10394
|
if (tracks.length === 0) {
|
|
10295
|
-
console.log("Nothing out there yet.");
|
|
10395
|
+
console.log(options.lens === "quarantine" ? "No wrong-audio captures." : "Nothing out there yet.");
|
|
10296
10396
|
return;
|
|
10297
10397
|
}
|
|
10298
10398
|
for (const track of tracks) {
|
|
10299
10399
|
const identity = `${track.artists.join(", ")} \u2014 ${track.title}`;
|
|
10400
|
+
if (options.lens === "quarantine") {
|
|
10401
|
+
const collided = track.nearestFinding;
|
|
10402
|
+
const why2 = collided ? `its audio came back as ${collided.logId ? `${collided.logId} ` : ""}${collided.artists.join(", ")} \u2014 ${collided.title}` : "its audio matched a track already in the archive";
|
|
10403
|
+
console.log(`wrong-audio ${identity.padEnd(52)} ${why2}`);
|
|
10404
|
+
continue;
|
|
10405
|
+
}
|
|
10300
10406
|
const dup = track.duplicateOf;
|
|
10301
10407
|
const dupWhy = dup ? `already in the archive \u2014 ${dup.logId ? `${dup.logId} ` : ""}${dup.artists.join(", ")} \u2014 ${dup.title}` : null;
|
|
10302
10408
|
if (options.lens === "capture") {
|
package/package.json
CHANGED