fluncle 0.91.0 → 0.93.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 +59 -11
  2. package/package.json +1 -1
package/bin/fluncle.mjs CHANGED
@@ -556,7 +556,7 @@ function parseVersion(version) {
556
556
  var currentVersion;
557
557
  var init_version = __esm(() => {
558
558
  init_output();
559
- currentVersion = "0.91.0".trim() ? "0.91.0".trim() : "0.1.0";
559
+ currentVersion = "0.93.0".trim() ? "0.93.0".trim() : "0.1.0";
560
560
  });
561
561
 
562
562
  // src/update-notifier.ts
@@ -2186,7 +2186,7 @@ function parseVersion2(version) {
2186
2186
  var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
2187
2187
  var init_version2 = __esm(() => {
2188
2188
  init_output();
2189
- currentVersion2 = "0.91.0".trim() ? "0.91.0".trim() : "0.1.0";
2189
+ currentVersion2 = "0.93.0".trim() ? "0.93.0".trim() : "0.1.0";
2190
2190
  });
2191
2191
 
2192
2192
  // ../../packages/registry/src/index.ts
@@ -2911,6 +2911,7 @@ __export(exports_track, {
2911
2911
  trackObserveCommand: () => trackObserveCommand,
2912
2912
  trackNoteCommand: () => trackNoteCommand,
2913
2913
  trackGetCommand: () => trackGetCommand,
2914
+ trackGetAdminCommand: () => trackGetAdminCommand,
2914
2915
  trackDraftCommand: () => trackDraftCommand,
2915
2916
  trackContextCommand: () => trackContextCommand,
2916
2917
  isPlatesOnlyUpload: () => isPlatesOnlyUpload,
@@ -2919,6 +2920,9 @@ __export(exports_track, {
2919
2920
  async function trackGetCommand(idOrLogId) {
2920
2921
  return publicApiGet(`/api/tracks/${encodeURIComponent(idOrLogId)}`);
2921
2922
  }
2923
+ async function trackGetAdminCommand(idOrLogId) {
2924
+ return adminApiGet(`/api/admin/tracks/${encodeURIComponent(idOrLogId)}`);
2925
+ }
2922
2926
  function isPlatesOnlyUpload(files) {
2923
2927
  const hasPlate = PLATE_FIELDS.some((option) => Boolean(files[option]));
2924
2928
  if (!hasPlate) {
@@ -3751,7 +3755,7 @@ async function uploadRenditionMultipart(masterPath, presignPath, onProgress = ()
3751
3755
  throw new CliError2("presign_missing", `Worker did not sign part ${part.partNumber} of ${plan.partCount}`);
3752
3756
  }
3753
3757
  onProgress(`Set video: part ${part.partNumber}/${plan.partCount}`);
3754
- const etag = await putPart(url, renditionPath, part);
3758
+ const etag = await putPart(url, renditionPath, part, onProgress);
3755
3759
  completed.push({ etag, partNumber: part.partNumber });
3756
3760
  }
3757
3761
  await completeUpload(presign.completeUrl, completed);
@@ -3764,14 +3768,32 @@ async function uploadRenditionMultipart(masterPath, presignPath, onProgress = ()
3764
3768
  rmSync2(renditionPath, { force: true });
3765
3769
  }
3766
3770
  }
3767
- async function putPart(url, path2, part) {
3768
- const response = await fetch(url, {
3769
- body: Bun.file(path2).slice(part.start, part.end),
3770
- method: "PUT"
3771
- });
3771
+ async function putPart(url, path2, part, onProgress = () => {}) {
3772
+ const body = await Bun.file(path2).slice(part.start, part.end).arrayBuffer();
3773
+ for (let attempt = 1;; attempt += 1) {
3774
+ try {
3775
+ return await putPartOnce(url, body, part);
3776
+ } catch (error) {
3777
+ if (error instanceof CliError2) {
3778
+ throw error;
3779
+ }
3780
+ if (attempt >= MAX_PART_ATTEMPTS) {
3781
+ throw new CliError2("r2_part_failed", `Part ${part.partNumber} failed after ${MAX_PART_ATTEMPTS} attempts: ${error instanceof Error ? error.message : String(error)}`);
3782
+ }
3783
+ const backoffMs = 500 * 2 ** (attempt - 1);
3784
+ onProgress(`Set video: part ${part.partNumber} dropped — retry ${attempt}/${MAX_PART_ATTEMPTS - 1} in ${backoffMs}ms…`);
3785
+ await new Promise((resolve) => setTimeout(resolve, backoffMs));
3786
+ }
3787
+ }
3788
+ }
3789
+ async function putPartOnce(url, body, part) {
3790
+ const response = await fetch(url, { body, method: "PUT" });
3772
3791
  if (!response.ok) {
3773
3792
  const detail = (await response.text().catch(() => "")).slice(0, 300);
3774
- throw new CliError2("r2_part_failed", `R2 rejected part ${part.partNumber} (${response.status} ${response.statusText})${detail ? `: ${detail}` : ""}`);
3793
+ if (response.status < 500) {
3794
+ throw new CliError2("r2_part_failed", `R2 rejected part ${part.partNumber} (${response.status} ${response.statusText})${detail ? `: ${detail}` : ""}`);
3795
+ }
3796
+ throw new Error(`R2 ${response.status} ${response.statusText} on part ${part.partNumber}${detail ? `: ${detail}` : ""}`);
3775
3797
  }
3776
3798
  const etag = response.headers.get("etag");
3777
3799
  if (!etag) {
@@ -3815,7 +3837,7 @@ async function deriveRendition(inputPath, outputPath) {
3815
3837
  throw new CliError2("ffmpeg_failed", `ffmpeg failed to derive the set-video rendition${detail ? `: ${detail}` : ""}`);
3816
3838
  }
3817
3839
  }
3818
- var FOUND_BASE2 = "https://found.fluncle.com", SET_VIDEO_RENDITION, DEFAULT_PART_SIZE, MIN_PART_SIZE, MAX_PARTS = 1e4;
3840
+ var FOUND_BASE2 = "https://found.fluncle.com", SET_VIDEO_RENDITION, DEFAULT_PART_SIZE, MIN_PART_SIZE, MAX_PARTS = 1e4, MAX_PART_ATTEMPTS = 5;
3819
3841
  var init_mixtape_set_video = __esm(() => {
3820
3842
  init_api();
3821
3843
  init_output();
@@ -3825,7 +3847,7 @@ var init_mixtape_set_video = __esm(() => {
3825
3847
  gopSeconds: 2,
3826
3848
  height: 1080
3827
3849
  };
3828
- DEFAULT_PART_SIZE = 100 * 1024 * 1024;
3850
+ DEFAULT_PART_SIZE = 16 * 1024 * 1024;
3829
3851
  MIN_PART_SIZE = 5 * 1024 * 1024;
3830
3852
  });
3831
3853
 
@@ -7329,6 +7351,10 @@ function addAdminCommands(program2) {
7329
7351
  await runAdminVehicles(options, vehiclesCommand2);
7330
7352
  });
7331
7353
  const adminTrack = adminTracks;
7354
+ adminTrack.command("get").description("Look up one finding by id or Log ID, with full admin fields").argument("[idOrLogId]").option("--json", "Print JSON", false).allowExcessArguments().action(async (idOrLogId, options) => {
7355
+ const { trackGetAdminCommand: trackGetAdminCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
7356
+ await runTrackGetAdmin(idOrLogId, options, trackGetAdminCommand2);
7357
+ });
7332
7358
  adminTrack.command("update").description("Certify a track into the archive").argument("[trackId]").option("--bpm <number>", "Track BPM").option("--features <json>", "Audio feature JSON").option("--json", "Print JSON", false).option("--key <key>", "Musical key").option("--note <text>", "Operator note").option("--status <status>", "Enrichment status").option("--video-url <url>", "Rendered video URL").allowExcessArguments().action(async (trackId, options) => {
7333
7359
  const { trackUpdateCommand: trackUpdateCommand2 } = await Promise.resolve().then(() => (init_track(), exports_track));
7334
7360
  await runTrackUpdate(trackId, options, trackUpdateCommand2);
@@ -7905,6 +7931,28 @@ async function runTrackGet(idOrLogId, options, trackGetCommand2) {
7905
7931
  console.log(`Log: ${t.logPageUrl}`);
7906
7932
  }
7907
7933
  }
7934
+ async function runTrackGetAdmin(idOrLogId, options, trackGetAdminCommand2) {
7935
+ if (!idOrLogId) {
7936
+ throw new Error("Missing id. Usage: fluncle admin tracks get <track_id|log_id> [--json]");
7937
+ }
7938
+ const result = await trackGetAdminCommand2(idOrLogId);
7939
+ if (options.json) {
7940
+ printJson(result);
7941
+ return;
7942
+ }
7943
+ const t = result.track;
7944
+ console.log(`${t.logId ? `${t.logId} ` : ""}${t.artists.join(", ")} \u2014 ${t.title}`);
7945
+ console.log([t.bpm ? `${t.bpm} bpm` : undefined, t.key, t.label, t.enrichmentStatus].filter(Boolean).join(" \xB7 "));
7946
+ const placed = t.galaxy && t.vibeX !== undefined && t.vibeY !== undefined;
7947
+ console.log(`Placement: ${placed && t.galaxy ? `${t.galaxy.name} (${t.vibeX?.toFixed(2)}, ${t.vibeY?.toFixed(2)})` : "unplaced"}`);
7948
+ console.log(`Video: ${t.videoUrl ? [t.videoSquaredAt ? "squared master" : "linked", t.videoVehicle].filter(Boolean).join(" \xB7 ") : "none (in the render queue)"}`);
7949
+ console.log(`Observation: ${t.observationAudioUrl ? `voiced${t.observationDurationMs ? ` \xB7 ${Math.round(t.observationDurationMs / 1000)}s` : ""}` : "none"}`);
7950
+ console.log(`Note: ${t.note ? t.note : "none"}`);
7951
+ console.log(`Spotify: ${t.spotifyUrl}`);
7952
+ if (t.logPageUrl) {
7953
+ console.log(`Log: ${t.logPageUrl}`);
7954
+ }
7955
+ }
7908
7956
  async function runTrackUpdate(trackId, options, trackUpdateCommand2) {
7909
7957
  if (!trackId) {
7910
7958
  throw new Error("Missing track id. Usage: fluncle admin tracks update <track_id>");
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.91.0"
34
+ "version": "0.93.0"
35
35
  }