fluncle 0.139.0 → 0.141.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 +204 -11
- 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.141.0".trim() ? "0.141.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.141.0".trim() ? "0.141.0".trim() : "0.1.0";
|
|
2193
2193
|
});
|
|
2194
2194
|
|
|
2195
2195
|
// ../../packages/registry/src/index.ts
|
|
@@ -2654,7 +2654,7 @@ var init_src = __esm(() => {
|
|
|
2654
2654
|
exposedContent: ["the XML sitemap index of every public page"],
|
|
2655
2655
|
kind: "discovery",
|
|
2656
2656
|
name: "discovery.sitemap",
|
|
2657
|
-
operatorNotes: "A sitemap INDEX, not a flat urlset: the URLs live in children at /sitemap/<kind>-<n>.xml (pages/findings/
|
|
2657
|
+
operatorNotes: "A sitemap INDEX, not a flat urlset: the URLs live in children at /sitemap/<kind>-<n>.xml, ONE CHILD PER ENTITY TYPE (pages/findings/artists/labels/albums/galaxies/logbook), each auto-paged under Google's 50,000-URL ceiling so a breach cannot happen rather than merely not having happened yet. robots.txt still names this one URL — a crawler discovers the children from here. Splitting per entity type is also the diagnostic: Search Console reports coverage PER sitemap, so each entity type gets its own submitted/indexed count.",
|
|
2658
2658
|
probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
|
|
2659
2659
|
route: "/sitemap.xml",
|
|
2660
2660
|
url: `${SITE}/sitemap.xml`,
|
|
@@ -2662,7 +2662,9 @@ var init_src = __esm(() => {
|
|
|
2662
2662
|
},
|
|
2663
2663
|
{
|
|
2664
2664
|
apiFormat: "application/xml",
|
|
2665
|
-
exposedContent: [
|
|
2665
|
+
exposedContent: [
|
|
2666
|
+
"one child sitemap, per entity type: the pages / findings / artists / labels / albums / galaxies / logbook URLs, auto-paged"
|
|
2667
|
+
],
|
|
2666
2668
|
kind: "discovery",
|
|
2667
2669
|
name: "discovery.sitemap-shard",
|
|
2668
2670
|
probeConfig: { cadenceMs: PROBE_CADENCE_MS, kind: "http", timeoutMs: PROBE_TIMEOUT_MS },
|
|
@@ -2956,6 +2958,17 @@ var init_src = __esm(() => {
|
|
|
2956
2958
|
probeConfig: { cadenceMs: 60 * MINUTE_MS, cronName: "fluncle-label-images", kind: "cron" },
|
|
2957
2959
|
weights: { status: "hidden" }
|
|
2958
2960
|
},
|
|
2961
|
+
{
|
|
2962
|
+
command: "fluncle admin backfills cover-masters",
|
|
2963
|
+
exposedContent: [
|
|
2964
|
+
"resolve each pending album/artist its OWN ≤1200² cover master (best source wins) → its own R2 image"
|
|
2965
|
+
],
|
|
2966
|
+
kind: "cron",
|
|
2967
|
+
name: "cron.cover-masters",
|
|
2968
|
+
operatorNotes: "every 60m, run by a rave-02 HOST systemd timer (docs/agents/hermes/cover-masters-timer/). The DURABLE other half of the album/artist cover (RFC musickit-second-authority U3b): the publish path + catalogue crawl MINT albums/artists (each `image_state='pending'`) and this cron gives each its OWN ≤1200²-capped cover derivative in R2 (found.fluncle.com, `albums/<slug>.<ext>` / `artists/<slug>.<ext>`) instead of hotlinking a third party — the label-logo posture, two entities over. IMAGE ONLY — a downscaled display derivative, reversible, the REF-05-conscious 1200 line (docs/album-artwork.md); it certifies nothing and publishes nothing (agent tier, the `backfill_label_images` precedent). Worker-paced: one bounded batch of albums (Apple template → Cover Art Archive → Spotify floor), then one of artists (Spotify floor), per tick; every rung requests a ≤1200 rendition and a byte read enforces the cap before the R2 put, so no un-downscaled original is ever stored. The `albums`/`artists` row carries the durable reliability state (image_state/image_attempted_at/image_failures), so a resolved/none entity is terminal. Served via Cloudflare Images `/cdn-cgi/image/…` (decision B). Zero LLM tokens. Source: docs/agents/hermes/scripts/cover-masters-sweep.*. See docs/album-artwork.md.",
|
|
2969
|
+
probeConfig: { cadenceMs: 60 * MINUTE_MS, cronName: "fluncle-cover-masters", kind: "cron" },
|
|
2970
|
+
weights: { status: "hidden" }
|
|
2971
|
+
},
|
|
2959
2972
|
{
|
|
2960
2973
|
command: "fluncle admin tracks capture-audio --queue",
|
|
2961
2974
|
exposedContent: [
|
|
@@ -3666,7 +3679,8 @@ __export(exports_admin_tracks, {
|
|
|
3666
3679
|
captureQueueCommand: () => captureQueueCommand,
|
|
3667
3680
|
backfillLastfmCommand: () => backfillLastfmCommand,
|
|
3668
3681
|
backfillDiscogsCommand: () => backfillDiscogsCommand,
|
|
3669
|
-
backfillAppleMusicCommand: () => backfillAppleMusicCommand
|
|
3682
|
+
backfillAppleMusicCommand: () => backfillAppleMusicCommand,
|
|
3683
|
+
backfillAppleCatalogueCommand: () => backfillAppleCatalogueCommand
|
|
3670
3684
|
});
|
|
3671
3685
|
async function fetchAdminTracks(options) {
|
|
3672
3686
|
const {
|
|
@@ -3846,6 +3860,10 @@ async function backfillAppleMusicCommand(limit, dryRun, cursor) {
|
|
|
3846
3860
|
}
|
|
3847
3861
|
return adminApiPost(`/api/admin/backfill/apple-music?${params.toString()}`);
|
|
3848
3862
|
}
|
|
3863
|
+
async function backfillAppleCatalogueCommand(limit, dryRun) {
|
|
3864
|
+
const params = new URLSearchParams({ dryRun: String(dryRun), limit: String(limit) });
|
|
3865
|
+
return adminApiPost(`/api/admin/backfill/apple-catalogue?${params.toString()}`);
|
|
3866
|
+
}
|
|
3849
3867
|
async function vehiclesCommand(limit) {
|
|
3850
3868
|
const tracks = await fetchAdminTracks({ hasVideo: true, max: limit, order: "desc" });
|
|
3851
3869
|
return tracks.map((track) => ({
|
|
@@ -5699,6 +5717,7 @@ var exports_admin_catalogue = {};
|
|
|
5699
5717
|
__export(exports_admin_catalogue, {
|
|
5700
5718
|
crawlStatusCommand: () => crawlStatusCommand,
|
|
5701
5719
|
crawlCatalogueCommand: () => crawlCatalogueCommand,
|
|
5720
|
+
clearWrongAudioCommand: () => clearWrongAudioCommand,
|
|
5702
5721
|
catalogueRankCommand: () => catalogueRankCommand,
|
|
5703
5722
|
catalogueListCommand: () => catalogueListCommand
|
|
5704
5723
|
});
|
|
@@ -5709,12 +5728,17 @@ async function catalogueRankCommand(options) {
|
|
|
5709
5728
|
}
|
|
5710
5729
|
async function catalogueListCommand(options) {
|
|
5711
5730
|
const params = new URLSearchParams;
|
|
5712
|
-
params.set("lens", options.lens === "capture" ? "capture" : "ear");
|
|
5731
|
+
params.set("lens", options.lens === "capture" ? "capture" : options.lens === "quarantine" ? "quarantine" : "ear");
|
|
5713
5732
|
if (options.limit) {
|
|
5714
5733
|
params.set("limit", options.limit);
|
|
5715
5734
|
}
|
|
5716
5735
|
return adminApiGet(`/api/admin/catalogue?${params.toString()}`);
|
|
5717
5736
|
}
|
|
5737
|
+
async function clearWrongAudioCommand(trackId) {
|
|
5738
|
+
return adminApiPost("/api/admin/catalogue/wrong-audio/clear", {
|
|
5739
|
+
trackId
|
|
5740
|
+
});
|
|
5741
|
+
}
|
|
5718
5742
|
async function crawlCatalogueCommand(limit, maxHop, dryRun) {
|
|
5719
5743
|
const params = new URLSearchParams({ limit: String(limit), maxHop: String(maxHop) });
|
|
5720
5744
|
if (dryRun) {
|
|
@@ -5781,6 +5805,22 @@ var init_admin_labels = __esm(() => {
|
|
|
5781
5805
|
init_api();
|
|
5782
5806
|
});
|
|
5783
5807
|
|
|
5808
|
+
// src/commands/admin-covers.ts
|
|
5809
|
+
var exports_admin_covers = {};
|
|
5810
|
+
__export(exports_admin_covers, {
|
|
5811
|
+
backfillCoverMastersCommand: () => backfillCoverMastersCommand
|
|
5812
|
+
});
|
|
5813
|
+
async function backfillCoverMastersCommand(kind, limit, dryRun, cursor) {
|
|
5814
|
+
const params = new URLSearchParams({ dryRun: String(dryRun), kind, limit: String(limit) });
|
|
5815
|
+
if (cursor) {
|
|
5816
|
+
params.set("cursor", cursor);
|
|
5817
|
+
}
|
|
5818
|
+
return adminApiPost(`/api/admin/backfill/cover-masters?${params.toString()}`);
|
|
5819
|
+
}
|
|
5820
|
+
var init_admin_covers = __esm(() => {
|
|
5821
|
+
init_api();
|
|
5822
|
+
});
|
|
5823
|
+
|
|
5784
5824
|
// src/commands/preview-migration.ts
|
|
5785
5825
|
var exports_preview_migration = {};
|
|
5786
5826
|
__export(exports_preview_migration, {
|
|
@@ -8761,10 +8801,19 @@ function addAdminCommands(program2) {
|
|
|
8761
8801
|
const { catalogueRankCommand: catalogueRankCommand2 } = await Promise.resolve().then(() => (init_admin_catalogue(), exports_admin_catalogue));
|
|
8762
8802
|
await runCatalogueRank(options, catalogueRankCommand2);
|
|
8763
8803
|
});
|
|
8764
|
-
catalogue.command("list").description("The ranked catalogue: closest to a finding (ear),
|
|
8804
|
+
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
8805
|
const { catalogueListCommand: catalogueListCommand2 } = await Promise.resolve().then(() => (init_admin_catalogue(), exports_admin_catalogue));
|
|
8766
8806
|
await runCatalogueList(options, catalogueListCommand2);
|
|
8767
8807
|
});
|
|
8808
|
+
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) => {
|
|
8809
|
+
const { clearWrongAudioCommand: clearWrongAudioCommand2 } = await Promise.resolve().then(() => (init_admin_catalogue(), exports_admin_catalogue));
|
|
8810
|
+
const { cleared } = await clearWrongAudioCommand2(trackId);
|
|
8811
|
+
if (options.json) {
|
|
8812
|
+
printJson({ cleared, ok: true });
|
|
8813
|
+
return;
|
|
8814
|
+
}
|
|
8815
|
+
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.`);
|
|
8816
|
+
});
|
|
8768
8817
|
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
8818
|
const { crawlCatalogueCommand: crawlCatalogueCommand2 } = await Promise.resolve().then(() => (init_admin_catalogue(), exports_admin_catalogue));
|
|
8770
8819
|
await runCrawlCatalogue(options, crawlCatalogueCommand2);
|
|
@@ -8793,6 +8842,10 @@ function addAdminCommands(program2) {
|
|
|
8793
8842
|
const { backfillAppleMusicCommand: backfillAppleMusicCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
8794
8843
|
await runBackfillAppleMusic(options, backfillAppleMusicCommand2);
|
|
8795
8844
|
});
|
|
8845
|
+
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) => {
|
|
8846
|
+
const { backfillAppleCatalogueCommand: backfillAppleCatalogueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
8847
|
+
await runBackfillAppleCatalogue(options, backfillAppleCatalogueCommand2);
|
|
8848
|
+
});
|
|
8796
8849
|
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
8850
|
const { backfillArtistsCommand: backfillArtistsCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
|
|
8798
8851
|
await runBackfillArtists(options, backfillArtistsCommand2);
|
|
@@ -8805,6 +8858,10 @@ function addAdminCommands(program2) {
|
|
|
8805
8858
|
const { backfillLabelImagesCommand: backfillLabelImagesCommand2 } = await Promise.resolve().then(() => (init_admin_labels(), exports_admin_labels));
|
|
8806
8859
|
await runBackfillLabelImages(options, backfillLabelImagesCommand2);
|
|
8807
8860
|
});
|
|
8861
|
+
backfill.command("cover-masters").description("Resolve owned \u22641200\xB2 cover masters (album/artist) into R2").option("--kind <kind>", "Which entity to drain: album | artist", "album").option("--dry-run", "Report the eligible worklist without any fetch or write", false).option("--limit <limit>", "Max entities to process", "50").option("--json", "Print JSON", false).action(async (options) => {
|
|
8862
|
+
const { backfillCoverMastersCommand: backfillCoverMastersCommand2 } = await Promise.resolve().then(() => (init_admin_covers(), exports_admin_covers));
|
|
8863
|
+
await runBackfillCoverMasters(options, backfillCoverMastersCommand2);
|
|
8864
|
+
});
|
|
8808
8865
|
artists.command("resolve").description("Resolve an artist's social identity (MB url-rels + Firecrawl gap-fill)").argument("[artistId]").option("--queue", "Show the resolve worklist (artists awaiting resolution), oldest first", false).option("--limit <limit>", "Number of artists to show with --queue", "50").option("--json", "Print JSON", false).allowExcessArguments().action(async (artistId, options) => {
|
|
8809
8866
|
if (options.queue) {
|
|
8810
8867
|
const { listArtistsCommand: listArtistsCommand2 } = await Promise.resolve().then(() => (init_admin_artists(), exports_admin_artists));
|
|
@@ -9213,23 +9270,30 @@ async function runBackfillAppleMusic(options, backfillAppleMusicCommand2) {
|
|
|
9213
9270
|
let cursor;
|
|
9214
9271
|
let dryRun = options.dryRun;
|
|
9215
9272
|
let throttled = false;
|
|
9273
|
+
let breakerTripped = false;
|
|
9216
9274
|
let configured = true;
|
|
9275
|
+
let albumFactsWritten = 0;
|
|
9217
9276
|
while (resolved.length + unresolved.length + failed.length < limit) {
|
|
9218
9277
|
const remaining = limit - (resolved.length + unresolved.length + failed.length);
|
|
9219
9278
|
const result = await backfillAppleMusicCommand2(remaining, options.dryRun, cursor);
|
|
9220
9279
|
dryRun = result.dryRun;
|
|
9221
9280
|
configured = result.configured;
|
|
9281
|
+
albumFactsWritten += result.albumFactsWritten;
|
|
9222
9282
|
resolved.push(...result.resolved);
|
|
9223
9283
|
unresolved.push(...result.unresolved);
|
|
9224
9284
|
failed.push(...result.failed);
|
|
9225
9285
|
skipped.push(...result.skipped);
|
|
9226
9286
|
if (!options.json) {
|
|
9227
9287
|
const verb2 = result.dryRun ? "would resolve" : "resolved";
|
|
9228
|
-
console.log(` \u2026${verb2} ${result.resolvedCount}; ${result.unresolvedCount} unresolved; ${result.failedCount} failed; ${result.skippedCount} skipped`);
|
|
9288
|
+
console.log(` \u2026${verb2} ${result.resolvedCount}; ${result.unresolvedCount} unresolved; ${result.failedCount} failed; ${result.skippedCount} skipped; ${result.albumFactsWritten} album facts`);
|
|
9229
9289
|
}
|
|
9230
9290
|
if (!result.configured) {
|
|
9231
9291
|
break;
|
|
9232
9292
|
}
|
|
9293
|
+
if (result.breakerTripped) {
|
|
9294
|
+
breakerTripped = true;
|
|
9295
|
+
break;
|
|
9296
|
+
}
|
|
9233
9297
|
if (result.rateLimited) {
|
|
9234
9298
|
throttled = true;
|
|
9235
9299
|
break;
|
|
@@ -9241,6 +9305,8 @@ async function runBackfillAppleMusic(options, backfillAppleMusicCommand2) {
|
|
|
9241
9305
|
}
|
|
9242
9306
|
if (options.json) {
|
|
9243
9307
|
printSweepJson({
|
|
9308
|
+
albumFactsWritten,
|
|
9309
|
+
breakerTripped,
|
|
9244
9310
|
configured,
|
|
9245
9311
|
dryRun,
|
|
9246
9312
|
failed,
|
|
@@ -9259,7 +9325,7 @@ async function runBackfillAppleMusic(options, backfillAppleMusicCommand2) {
|
|
|
9259
9325
|
return;
|
|
9260
9326
|
}
|
|
9261
9327
|
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.`);
|
|
9328
|
+
console.log(`${verb} ${resolved.length} Apple Music URL(s); ${unresolved.length} unresolved; ${failed.length} failed; ${skipped.length} skipped; ${albumFactsWritten} album facts.`);
|
|
9263
9329
|
for (const item of resolved) {
|
|
9264
9330
|
console.log(` ${item.logId}: ${item.url}`);
|
|
9265
9331
|
}
|
|
@@ -9270,6 +9336,72 @@ async function runBackfillAppleMusic(options, backfillAppleMusicCommand2) {
|
|
|
9270
9336
|
process.exitCode = 1;
|
|
9271
9337
|
}
|
|
9272
9338
|
}
|
|
9339
|
+
async function runBackfillAppleCatalogue(options, backfillAppleCatalogueCommand2) {
|
|
9340
|
+
const limit = parseListLimit(options.limit);
|
|
9341
|
+
const resolved = [];
|
|
9342
|
+
const unresolved = [];
|
|
9343
|
+
const failed = [];
|
|
9344
|
+
let dryRun = options.dryRun;
|
|
9345
|
+
let throttled = false;
|
|
9346
|
+
let breakerTripped = false;
|
|
9347
|
+
let configured = true;
|
|
9348
|
+
let albumFactsWritten = 0;
|
|
9349
|
+
while (resolved.length + unresolved.length + failed.length < limit) {
|
|
9350
|
+
const remaining = limit - (resolved.length + unresolved.length + failed.length);
|
|
9351
|
+
const result = await backfillAppleCatalogueCommand2(remaining, options.dryRun);
|
|
9352
|
+
dryRun = result.dryRun;
|
|
9353
|
+
configured = result.configured;
|
|
9354
|
+
albumFactsWritten += result.albumFactsWritten;
|
|
9355
|
+
resolved.push(...result.resolved);
|
|
9356
|
+
unresolved.push(...result.unresolved);
|
|
9357
|
+
failed.push(...result.failed);
|
|
9358
|
+
if (!options.json) {
|
|
9359
|
+
const verb2 = result.dryRun ? "would resolve" : "resolved";
|
|
9360
|
+
console.log(` \u2026${verb2} ${result.resolvedCount}; ${result.unresolvedCount} unresolved; ${result.failedCount} failed; ${result.albumFactsWritten} album facts`);
|
|
9361
|
+
}
|
|
9362
|
+
if (!result.configured) {
|
|
9363
|
+
break;
|
|
9364
|
+
}
|
|
9365
|
+
if (result.breakerTripped) {
|
|
9366
|
+
breakerTripped = true;
|
|
9367
|
+
break;
|
|
9368
|
+
}
|
|
9369
|
+
if (result.rateLimited) {
|
|
9370
|
+
throttled = true;
|
|
9371
|
+
break;
|
|
9372
|
+
}
|
|
9373
|
+
if (result.resolvedCount === 0 && result.unresolvedCount === 0 && result.failedCount === 0) {
|
|
9374
|
+
break;
|
|
9375
|
+
}
|
|
9376
|
+
}
|
|
9377
|
+
if (options.json) {
|
|
9378
|
+
printSweepJson({
|
|
9379
|
+
albumFactsWritten,
|
|
9380
|
+
breakerTripped,
|
|
9381
|
+
configured,
|
|
9382
|
+
dryRun,
|
|
9383
|
+
failed,
|
|
9384
|
+
rateLimited: throttled,
|
|
9385
|
+
resolved,
|
|
9386
|
+
resolvedCount: resolved.length,
|
|
9387
|
+
unresolved,
|
|
9388
|
+
unresolvedCount: unresolved.length
|
|
9389
|
+
}, failed.length);
|
|
9390
|
+
return;
|
|
9391
|
+
}
|
|
9392
|
+
if (!configured) {
|
|
9393
|
+
console.log("Apple catalogue backfill is not configured (the Worker's MusicKit secrets are unset) \u2014 nothing resolved.");
|
|
9394
|
+
return;
|
|
9395
|
+
}
|
|
9396
|
+
const verb = dryRun ? "Would resolve" : "Resolved";
|
|
9397
|
+
console.log(`${verb} ${resolved.length} catalogue Apple URL(s); ${unresolved.length} unresolved; ${failed.length} failed; ${albumFactsWritten} album facts.`);
|
|
9398
|
+
for (const item of failed) {
|
|
9399
|
+
console.log(` ${item.trackId}: ${item.error}`);
|
|
9400
|
+
}
|
|
9401
|
+
if (failed.length > 0) {
|
|
9402
|
+
process.exitCode = 1;
|
|
9403
|
+
}
|
|
9404
|
+
}
|
|
9273
9405
|
async function runBackfillArtists(options, backfillArtistsCommand2) {
|
|
9274
9406
|
const limit = parseListLimit(options.limit);
|
|
9275
9407
|
const upserted = [];
|
|
@@ -9362,6 +9494,60 @@ async function runBackfillArtistImages(options, backfillArtistImagesCommand2) {
|
|
|
9362
9494
|
process.exitCode = 1;
|
|
9363
9495
|
}
|
|
9364
9496
|
}
|
|
9497
|
+
async function runBackfillCoverMasters(options, backfillCoverMastersCommand2) {
|
|
9498
|
+
const limit = parseListLimit(options.limit);
|
|
9499
|
+
const kind = options.kind === "artist" ? "artist" : "album";
|
|
9500
|
+
const resolved = [];
|
|
9501
|
+
const none = [];
|
|
9502
|
+
const failed = [];
|
|
9503
|
+
let cursor;
|
|
9504
|
+
let dryRun = options.dryRun;
|
|
9505
|
+
while (resolved.length + none.length + failed.length < limit) {
|
|
9506
|
+
const remaining = limit - (resolved.length + none.length + failed.length);
|
|
9507
|
+
const result = await backfillCoverMastersCommand2(kind, remaining, options.dryRun, cursor);
|
|
9508
|
+
dryRun = result.dryRun;
|
|
9509
|
+
resolved.push(...result.resolved);
|
|
9510
|
+
none.push(...result.none);
|
|
9511
|
+
failed.push(...result.failed);
|
|
9512
|
+
if (!options.json) {
|
|
9513
|
+
const verb2 = result.dryRun ? "would resolve" : "resolved";
|
|
9514
|
+
console.log(` \u2026${verb2} ${result.resolvedCount}; ${result.noneCount} without a source; ${result.failedCount} failed`);
|
|
9515
|
+
}
|
|
9516
|
+
if (result.nextCursor === null) {
|
|
9517
|
+
break;
|
|
9518
|
+
}
|
|
9519
|
+
cursor = result.nextCursor;
|
|
9520
|
+
}
|
|
9521
|
+
if (options.json) {
|
|
9522
|
+
printJson({
|
|
9523
|
+
dryRun,
|
|
9524
|
+
failed,
|
|
9525
|
+
failedCount: failed.length,
|
|
9526
|
+
kind,
|
|
9527
|
+
none,
|
|
9528
|
+
noneCount: none.length,
|
|
9529
|
+
ok: true,
|
|
9530
|
+
rateLimited: false,
|
|
9531
|
+
resolved,
|
|
9532
|
+
resolvedCount: resolved.length
|
|
9533
|
+
});
|
|
9534
|
+
if (failed.length > 0) {
|
|
9535
|
+
process.exitCode = 1;
|
|
9536
|
+
}
|
|
9537
|
+
return;
|
|
9538
|
+
}
|
|
9539
|
+
const verb = dryRun ? "Would resolve" : "Resolved";
|
|
9540
|
+
console.log(`${verb} ${resolved.length} ${kind} cover master(s); ${none.length} without a source; ${failed.length} failed.`);
|
|
9541
|
+
for (const slug of resolved) {
|
|
9542
|
+
console.log(` ${slug}`);
|
|
9543
|
+
}
|
|
9544
|
+
for (const item of failed) {
|
|
9545
|
+
console.log(` ${item.slug}: ${item.error}`);
|
|
9546
|
+
}
|
|
9547
|
+
if (failed.length > 0) {
|
|
9548
|
+
process.exitCode = 1;
|
|
9549
|
+
}
|
|
9550
|
+
}
|
|
9365
9551
|
async function runBackfillLabelImages(options, backfillLabelImagesCommand2) {
|
|
9366
9552
|
const limit = parseListLimit(options.limit);
|
|
9367
9553
|
const resolved = [];
|
|
@@ -10280,7 +10466,8 @@ async function runCatalogueRank(options, catalogueRankCommand2) {
|
|
|
10280
10466
|
printJson({ ok: true, summary });
|
|
10281
10467
|
return;
|
|
10282
10468
|
}
|
|
10283
|
-
|
|
10469
|
+
const quarantine = summary.quarantined > 0 ? ` Quarantined ${summary.quarantined} as wrong audio.` : "";
|
|
10470
|
+
console.log(`Ranked ${summary.scored} against ${summary.embeddedFindings} embedded findings; prioritized ${summary.prioritized} for capture.${quarantine} ${summary.remaining} still stale.`);
|
|
10284
10471
|
}
|
|
10285
10472
|
async function runCatalogueList(options, catalogueListCommand2) {
|
|
10286
10473
|
const { summary, tracks } = await catalogueListCommand2({
|
|
@@ -10292,11 +10479,17 @@ async function runCatalogueList(options, catalogueListCommand2) {
|
|
|
10292
10479
|
return;
|
|
10293
10480
|
}
|
|
10294
10481
|
if (tracks.length === 0) {
|
|
10295
|
-
console.log("Nothing out there yet.");
|
|
10482
|
+
console.log(options.lens === "quarantine" ? "No wrong-audio captures." : "Nothing out there yet.");
|
|
10296
10483
|
return;
|
|
10297
10484
|
}
|
|
10298
10485
|
for (const track of tracks) {
|
|
10299
10486
|
const identity = `${track.artists.join(", ")} \u2014 ${track.title}`;
|
|
10487
|
+
if (options.lens === "quarantine") {
|
|
10488
|
+
const collided = track.nearestFinding;
|
|
10489
|
+
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";
|
|
10490
|
+
console.log(`wrong-audio ${identity.padEnd(52)} ${why2}`);
|
|
10491
|
+
continue;
|
|
10492
|
+
}
|
|
10300
10493
|
const dup = track.duplicateOf;
|
|
10301
10494
|
const dupWhy = dup ? `already in the archive \u2014 ${dup.logId ? `${dup.logId} ` : ""}${dup.artists.join(", ")} \u2014 ${dup.title}` : null;
|
|
10302
10495
|
if (options.lens === "capture") {
|
package/package.json
CHANGED