fluncle 0.235.0 → 0.237.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.
Files changed (2) hide show
  1. package/bin/fluncle.mjs +41 -8
  2. package/package.json +1 -1
package/bin/fluncle.mjs CHANGED
@@ -622,7 +622,7 @@ function parseVersion(version) {
622
622
  var currentVersion;
623
623
  var init_version = __esm(() => {
624
624
  init_output();
625
- currentVersion = "0.235.0".trim() ? "0.235.0".trim() : "0.1.0";
625
+ currentVersion = "0.237.0".trim() ? "0.237.0".trim() : "0.1.0";
626
626
  });
627
627
 
628
628
  // src/update-notifier.ts
@@ -2335,7 +2335,7 @@ function parseVersion2(version) {
2335
2335
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
2336
2336
  var init_version2 = __esm(() => {
2337
2337
  init_output();
2338
- currentVersion2 = "0.235.0".trim() ? "0.235.0".trim() : "0.1.0";
2338
+ currentVersion2 = "0.237.0".trim() ? "0.237.0".trim() : "0.1.0";
2339
2339
  });
2340
2340
 
2341
2341
  // ../../packages/registry/src/index.ts
@@ -11433,7 +11433,7 @@ var init_admin_bios = __esm(() => {
11433
11433
  });
11434
11434
 
11435
11435
  // ../../packages/contracts/src/orpc/admin-catalogue.ts
11436
- var CatalogueLensSchema, CapturePriorityReasonSchema, CatalogueMatchSchema, CatalogueTrackItemSchema, CatalogueSummarySchema, listCatalogueTracks, rankCatalogue, recordDemand, clearWrongAudio, requeueUnmatchedCaptures, requeueAnchor, flagWrongAudio, forceCapture, certifyTrack, setTrackDismissed, CaptureVerifyItemSchema, listUnverifiedCaptures, verifyCapture, CrawlPassSchema, CrawlPhaseInitializationSchema, CrawlPhaseInputSchema, CrawlPhaseOutputSchema, CrawlStatusSchema, crawlCatalogue, getCrawlStatus, ANCHOR_CANDIDATE_LIMIT = 100, ANCHOR_ARTIST_LIMIT = 20, ANCHOR_TEXT_MAX = 300, ANCHOR_ISRC_MAX = 64, ANCHOR_ID_MAX = 64, ANCHOR_URL_MAX = 2048, AnchorCandidateSchema, anchorTrack, DEEZER_CANDIDATE_LIMIT = 5, DEEZER_TEXT_MAX = 300, DEEZER_ISRC_MAX = 64, DEEZER_TRACK_ID_MAX = 32, DeezerIsrcCandidateSchema, resolveAnchor, resolveAnchorReview, setAnchorSearch, setAnchorApify, CaptureBudgetStateSchema, getCaptureBudget, setCaptureBudget, SpotifyAnchorBreakerStateSchema, getSpotifyAnchorBreaker, resetSpotifyAnchorBreaker, AppleBreakerStateSchema, resetAppleBreaker, adminCatalogueContract;
11436
+ var CatalogueLensSchema, CapturePriorityReasonSchema, CatalogueMatchSchema, CatalogueTrackItemSchema, CatalogueSummarySchema, listCatalogueTracks, rankCatalogue, recordDemand, clearWrongAudio, requeueUnmatchedCaptures, requeueAnchor, requeueIsrcRecovery, flagWrongAudio, forceCapture, certifyTrack, setTrackDismissed, CaptureVerifyItemSchema, listUnverifiedCaptures, verifyCapture, CrawlPassSchema, CrawlPhaseInitializationSchema, MAX_CRAWL_PREPARE_LIMIT = 6, CrawlPhaseInputSchema, CrawlPhaseOutputSchema, CrawlStatusSchema, crawlCatalogue, getCrawlStatus, ANCHOR_CANDIDATE_LIMIT = 100, ANCHOR_ARTIST_LIMIT = 20, ANCHOR_TEXT_MAX = 300, ANCHOR_ISRC_MAX = 64, ANCHOR_ID_MAX = 64, ANCHOR_URL_MAX = 2048, AnchorCandidateSchema, anchorTrack, DEEZER_CANDIDATE_LIMIT = 5, DEEZER_TEXT_MAX = 300, DEEZER_ISRC_MAX = 64, DEEZER_TRACK_ID_MAX = 32, DeezerIsrcCandidateSchema, resolveAnchor, resolveAnchorReview, setAnchorSearch, setAnchorApify, CaptureBudgetStateSchema, getCaptureBudget, setCaptureBudget, SpotifyAnchorBreakerStateSchema, getSpotifyAnchorBreaker, resetSpotifyAnchorBreaker, AppleBreakerStateSchema, resetAppleBreaker, adminCatalogueContract;
11437
11437
  var init_admin_catalogue = __esm(() => {
11438
11438
  init_dist3();
11439
11439
  init_zod();
@@ -11562,6 +11562,21 @@ var init_admin_catalogue = __esm(() => {
11562
11562
  summary: "Clear named rows' anchor re-ask backoff so the next tick retries them (operator)",
11563
11563
  tags: ["Admin"]
11564
11564
  }).input(object({ trackIds: array(string2().min(1)).min(1).max(250) })).output(object({ ok: literal(true), requeued: number2() }));
11565
+ requeueIsrcRecovery = oc.route({
11566
+ method: "POST",
11567
+ operationId: "requeueIsrcRecovery",
11568
+ path: "/admin/catalogue/isrc-recovery/requeue",
11569
+ summary: "Clear Deezer-empty ISRC-recovery stamps from a named window (operator)",
11570
+ tags: ["Admin"]
11571
+ }).input(object({
11572
+ dryRun: boolean2().default(true),
11573
+ since: string2().regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{1,3})?Z)?$/, "since must be an ISO date (2026-09-09) or instant (2026-09-09T00:00:00.000Z)")
11574
+ })).output(object({
11575
+ dryRun: boolean2(),
11576
+ matched: number2(),
11577
+ ok: literal(true),
11578
+ requeued: number2()
11579
+ }));
11565
11580
  flagWrongAudio = oc.route({
11566
11581
  method: "POST",
11567
11582
  operationId: "flagWrongAudio",
@@ -11663,7 +11678,7 @@ var init_admin_catalogue = __esm(() => {
11663
11678
  CrawlPhaseInputSchema = discriminatedUnion("phase", [
11664
11679
  object({ phase: literal("initialize") }),
11665
11680
  object({
11666
- limit: number2().int().min(1).max(2).default(2),
11681
+ limit: number2().int().min(1).max(MAX_CRAWL_PREPARE_LIMIT).default(2),
11667
11682
  maxHop: number2().int().min(0).max(3).default(2),
11668
11683
  phase: literal("prepare")
11669
11684
  }),
@@ -11686,7 +11701,7 @@ var init_admin_catalogue = __esm(() => {
11686
11701
  object({
11687
11702
  frontierPending: number2(),
11688
11703
  initialization: CrawlPhaseInitializationSchema,
11689
- items: array(object({ nodeId: string2(), preparedToken: string2() })).max(2),
11704
+ items: array(object({ nodeId: string2(), preparedToken: string2() })).max(MAX_CRAWL_PREPARE_LIMIT),
11690
11705
  kind: _enum(["drained", "prepared", "unavailable"]),
11691
11706
  ok: literal(true),
11692
11707
  phase: literal("prepare")
@@ -11733,7 +11748,8 @@ var init_admin_catalogue = __esm(() => {
11733
11748
  release: number2()
11734
11749
  }),
11735
11750
  labelsUndecided: number2(),
11736
- seedLabels: array(string2())
11751
+ seedLabels: array(string2()),
11752
+ storablePending: number2()
11737
11753
  }).meta({ id: "CrawlStatus" });
11738
11754
  crawlCatalogue = oc.route({
11739
11755
  inputStructure: "detailed",
@@ -11934,6 +11950,7 @@ var init_admin_catalogue = __esm(() => {
11934
11950
  rank_catalogue: rankCatalogue,
11935
11951
  record_demand: recordDemand,
11936
11952
  requeue_anchor: requeueAnchor,
11953
+ requeue_isrc_recovery: requeueIsrcRecovery,
11937
11954
  requeue_unmatched_captures: requeueUnmatchedCaptures,
11938
11955
  reset_apple_breaker: resetAppleBreaker,
11939
11956
  reset_spotify_anchor_breaker: resetSpotifyAnchorBreaker,
@@ -17235,10 +17252,10 @@ async function resolveDeezerSearch(track) {
17235
17252
  return;
17236
17253
  }
17237
17254
  try {
17238
- const query = `artist:"${artist}" track:"${stripVersionSuffix(track.title.trim())}"`;
17255
+ const query = `${artist} ${stripVersionSuffix(track.title.trim())}`;
17239
17256
  const response = await fetch(`https://api.deezer.com/search?q=${encodeURIComponent(query)}`);
17240
17257
  const body = await response.json();
17241
- const hit = (body.data ?? []).find((item) => item.preview && versionMatches(track.title, item.title ?? "") && baseTitleMatches(track.title, item.title ?? "") && durationAgrees(track.durationMs, item.duration));
17258
+ const hit = (body.data ?? []).find((item) => item.preview && normalize(item.artist?.name ?? "").includes(normalize(artist)) && versionMatches(track.title, item.title ?? "") && baseTitleMatches(track.title, item.title ?? "") && durationAgrees(track.durationMs, item.duration));
17242
17259
  return downloadPreview(hit?.preview, "deezer:search");
17243
17260
  } catch {
17244
17261
  return;
@@ -18510,6 +18527,9 @@ async function requeueAnchorCommand(trackIds) {
18510
18527
  trackIds
18511
18528
  });
18512
18529
  }
18530
+ async function requeueIsrcRecoveryCommand(input) {
18531
+ return adminApiPost("/api/v1/admin/catalogue/isrc-recovery/requeue", input);
18532
+ }
18513
18533
  async function forceCaptureCommand(trackId) {
18514
18534
  return adminApiPost("/api/v1/admin/catalogue/force-capture", {
18515
18535
  trackId
@@ -22166,6 +22186,18 @@ JSON field reference:
22166
22186
  }
22167
22187
  console.log(`Re-queued ${requeued} of ${trackIds.length} row(s) \u2014 the next anchor tick picks them up at their real priority.`);
22168
22188
  });
22189
+ catalogue.command("requeue-isrc-recovery").description("Clear Deezer-empty ISRC-recovery stamps from a window (dry-run; --apply to take)").requiredOption("--since <iso>", "Inclusive lower bound on the stamp (2026-09-09)").option("--apply", "Actually clear the stamps (default is a dry-run count)", false).option("--json", "Print JSON", false).action(async (options) => {
22190
+ await Promise.resolve().then(() => init_admin_catalogue2());
22191
+ const result = await requeueIsrcRecoveryCommand({
22192
+ dryRun: options.apply !== true,
22193
+ since: options.since
22194
+ });
22195
+ if (options.json) {
22196
+ printJson({ ok: true, ...result });
22197
+ return;
22198
+ }
22199
+ console.log(result.dryRun ? `DRY RUN \u2014 ${result.matched} row(s) stamped Deezer-empty since ${options.since}. Re-run with --apply to clear them.` : `Cleared ${result.requeued} of ${result.matched} row(s) \u2014 the next isrc-recovery tick asks them again.`);
22200
+ });
22169
22201
  catalogue.command("requeue-unmatched").description("Re-queue terminal-unmatched captures after a matcher improvement (operator)").option("--json", "Print JSON", false).action(async (options) => {
22170
22202
  await Promise.resolve().then(() => init_admin_catalogue2());
22171
22203
  const { requeued, skippedVetoed } = await requeueUnmatchedCommand();
@@ -22904,6 +22936,7 @@ async function runCrawlStatus(options, crawlStatusCommand) {
22904
22936
  const { frontier, frontierByKind } = result;
22905
22937
  console.log(`Catalogue: ${result.catalogueTracks} uncertified track(s); ${result.anchorsPending} awaiting a Spotify anchor.`);
22906
22938
  console.log(`Frontier: ${frontier.pending} pending, ${frontier.done} done, ${frontier.failed} failed, ${frontier.skipped} skipped (${frontierByKind.label} label, ${frontierByKind.artist} artist, ${frontierByKind.release} release).`);
22939
+ console.log(`Ready to write: ${result.storablePending} storable release node(s) claimable now.`);
22907
22940
  console.log(`Seeds (${result.seedLabels.length}): ${result.seedLabels.join(", ") || "none enabled"}.`);
22908
22941
  console.log(`Awaiting your ruling: ${result.labelsUndecided} label(s).`);
22909
22942
  }
package/package.json CHANGED
@@ -31,5 +31,5 @@
31
31
  "url": "git+https://github.com/mauricekleine/fluncle.git"
32
32
  },
33
33
  "type": "module",
34
- "version": "0.235.0"
34
+ "version": "0.237.0"
35
35
  }