fluncle 0.81.0 → 0.83.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 +75 -162
- package/package.json +1 -1
package/bin/fluncle.mjs
CHANGED
|
@@ -462,26 +462,6 @@ function versionMatches(findingTitle, candidateTitle) {
|
|
|
462
462
|
}
|
|
463
463
|
return !candidateIsRemix;
|
|
464
464
|
}
|
|
465
|
-
function trackLabel(artists, title) {
|
|
466
|
-
const joined = artists.join(", ");
|
|
467
|
-
return joined && title ? `${joined} — ${title}` : joined || title;
|
|
468
|
-
}
|
|
469
|
-
function resolveClipTracks(options) {
|
|
470
|
-
const { inMs, members, outMs, setDurationMs } = options;
|
|
471
|
-
const cued = members.filter((member) => member.startMs != null).sort((a, b) => a.startMs - b.startMs);
|
|
472
|
-
if (cued.length === 0) {
|
|
473
|
-
return [];
|
|
474
|
-
}
|
|
475
|
-
const lastIndex = cued.length - 1;
|
|
476
|
-
return cued.filter((member, index) => {
|
|
477
|
-
const lo = index === 0 ? Math.min(member.startMs, inMs) : member.startMs;
|
|
478
|
-
const hi = index === lastIndex ? Math.max(setDurationMs, outMs) : cued[index + 1]?.startMs ?? Number.POSITIVE_INFINITY;
|
|
479
|
-
return lo < outMs && inMs < hi;
|
|
480
|
-
}).map((member) => ({
|
|
481
|
-
label: trackLabel(member.artists, member.title),
|
|
482
|
-
startMs: member.startMs
|
|
483
|
-
}));
|
|
484
|
-
}
|
|
485
465
|
function mixcloudSections(members) {
|
|
486
466
|
return members.filter((member) => member.startMs != null).sort((a, b) => a.startMs - b.startMs).map((member) => ({
|
|
487
467
|
artist: member.artists.join(", "),
|
|
@@ -558,7 +538,7 @@ function parseVersion(version) {
|
|
|
558
538
|
var currentVersion;
|
|
559
539
|
var init_version = __esm(() => {
|
|
560
540
|
init_output();
|
|
561
|
-
currentVersion = "0.
|
|
541
|
+
currentVersion = "0.83.0".trim() ? "0.83.0".trim() : "0.1.0";
|
|
562
542
|
});
|
|
563
543
|
|
|
564
544
|
// src/update-notifier.ts
|
|
@@ -2466,7 +2446,7 @@ function parseVersion2(version) {
|
|
|
2466
2446
|
var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
|
|
2467
2447
|
var init_version2 = __esm(() => {
|
|
2468
2448
|
init_output();
|
|
2469
|
-
currentVersion2 = "0.
|
|
2449
|
+
currentVersion2 = "0.83.0".trim() ? "0.83.0".trim() : "0.1.0";
|
|
2470
2450
|
});
|
|
2471
2451
|
|
|
2472
2452
|
// ../../packages/registry/src/index.ts
|
|
@@ -3385,13 +3365,24 @@ __export(exports_admin_tracks, {
|
|
|
3385
3365
|
queueCommand: () => queueCommand,
|
|
3386
3366
|
observeQueueCommand: () => observeQueueCommand,
|
|
3387
3367
|
noteQueueCommand: () => noteQueueCommand,
|
|
3368
|
+
listCommand: () => listCommand,
|
|
3388
3369
|
enrichQueueCommand: () => enrichQueueCommand,
|
|
3389
3370
|
contextQueueCommand: () => contextQueueCommand,
|
|
3390
3371
|
backfillLastfmCommand: () => backfillLastfmCommand,
|
|
3391
3372
|
backfillDiscogsCommand: () => backfillDiscogsCommand
|
|
3392
3373
|
});
|
|
3393
3374
|
async function fetchAdminTracks(options) {
|
|
3394
|
-
const {
|
|
3375
|
+
const {
|
|
3376
|
+
hasContext,
|
|
3377
|
+
hasKey,
|
|
3378
|
+
hasNote,
|
|
3379
|
+
hasObservation,
|
|
3380
|
+
hasVideo,
|
|
3381
|
+
max,
|
|
3382
|
+
order,
|
|
3383
|
+
retryEmptyContext,
|
|
3384
|
+
status
|
|
3385
|
+
} = options;
|
|
3395
3386
|
const results = [];
|
|
3396
3387
|
let cursor;
|
|
3397
3388
|
do {
|
|
@@ -3399,6 +3390,9 @@ async function fetchAdminTracks(options) {
|
|
|
3399
3390
|
if (hasVideo !== undefined) {
|
|
3400
3391
|
params.set("hasVideo", String(hasVideo));
|
|
3401
3392
|
}
|
|
3393
|
+
if (hasKey !== undefined) {
|
|
3394
|
+
params.set("hasKey", String(hasKey));
|
|
3395
|
+
}
|
|
3402
3396
|
if (hasContext !== undefined) {
|
|
3403
3397
|
params.set("hasContext", String(hasContext));
|
|
3404
3398
|
}
|
|
@@ -3432,6 +3426,13 @@ async function fetchAdminTracks(options) {
|
|
|
3432
3426
|
} while (cursor);
|
|
3433
3427
|
return results;
|
|
3434
3428
|
}
|
|
3429
|
+
async function listCommand(options) {
|
|
3430
|
+
return fetchAdminTracks({
|
|
3431
|
+
hasKey: options.hasKey,
|
|
3432
|
+
max: options.limit,
|
|
3433
|
+
order: options.order
|
|
3434
|
+
});
|
|
3435
|
+
}
|
|
3435
3436
|
async function queueCommand(limit, filters = {}) {
|
|
3436
3437
|
return fetchAdminTracks({
|
|
3437
3438
|
hasContext: true,
|
|
@@ -4250,135 +4251,32 @@ var init_mixtapes2 = __esm(() => {
|
|
|
4250
4251
|
var exports_clips = {};
|
|
4251
4252
|
__export(exports_clips, {
|
|
4252
4253
|
setVideoUrl: () => setVideoUrl,
|
|
4253
|
-
resolveClipSansFontFile: () => resolveClipSansFontFile,
|
|
4254
|
-
resolveClipFontFile: () => resolveClipFontFile,
|
|
4255
|
-
escapeDrawtextValue: () => escapeDrawtextValue,
|
|
4256
4254
|
clipsListCommand: () => clipsListCommand,
|
|
4257
4255
|
clipFootageKey: () => clipFootageKey,
|
|
4258
4256
|
clipCutFilterComplex: () => clipCutFilterComplex,
|
|
4259
4257
|
clipCutFfmpegArgs: () => clipCutFfmpegArgs,
|
|
4260
4258
|
clipCutCommand: () => clipCutCommand,
|
|
4261
|
-
brandDrawtext: () => brandDrawtext,
|
|
4262
4259
|
MAX_CLIP_BYTES: () => MAX_CLIP_BYTES,
|
|
4263
4260
|
CLIP_WIDTH: () => CLIP_WIDTH,
|
|
4264
|
-
CLIP_TITLE_SIZE: () => CLIP_TITLE_SIZE,
|
|
4265
|
-
CLIP_TITLE_COLOR: () => CLIP_TITLE_COLOR,
|
|
4266
|
-
CLIP_SHARP_BORDERW: () => CLIP_SHARP_BORDERW,
|
|
4267
|
-
CLIP_SAFE_BOTTOM: () => CLIP_SAFE_BOTTOM,
|
|
4268
4261
|
CLIP_MAXRATE: () => CLIP_MAXRATE,
|
|
4269
|
-
CLIP_MARGIN_X: () => CLIP_MARGIN_X,
|
|
4270
|
-
CLIP_LINE_GAP: () => CLIP_LINE_GAP,
|
|
4271
4262
|
CLIP_HEIGHT: () => CLIP_HEIGHT,
|
|
4272
|
-
CLIP_HALO_FEATHER_SIGMA: () => CLIP_HALO_FEATHER_SIGMA,
|
|
4273
|
-
CLIP_HALO_CORE_SIGMA: () => CLIP_HALO_CORE_SIGMA,
|
|
4274
|
-
CLIP_HALO_COLOR: () => CLIP_HALO_COLOR,
|
|
4275
4263
|
CLIP_CRF: () => CLIP_CRF,
|
|
4276
|
-
CLIP_COORDINATE_SIZE: () => CLIP_COORDINATE_SIZE,
|
|
4277
|
-
CLIP_COORDINATE_COLOR: () => CLIP_COORDINATE_COLOR,
|
|
4278
4264
|
CLIP_BUFSIZE: () => CLIP_BUFSIZE,
|
|
4279
|
-
CLIP_AUDIO_BITRATE: () => CLIP_AUDIO_BITRATE
|
|
4280
|
-
BOX_CLIP_SANS_FONT_FILE: () => BOX_CLIP_SANS_FONT_FILE,
|
|
4281
|
-
BOX_CLIP_FONT_FILE: () => BOX_CLIP_FONT_FILE
|
|
4265
|
+
CLIP_AUDIO_BITRATE: () => CLIP_AUDIO_BITRATE
|
|
4282
4266
|
});
|
|
4283
4267
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
4284
|
-
import {
|
|
4268
|
+
import { rmSync as rmSync3, statSync as statSync4 } from "node:fs";
|
|
4285
4269
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
4286
4270
|
import { join as join5 } from "node:path";
|
|
4287
4271
|
function clipFootageKey(clipId) {
|
|
4288
4272
|
return `${clipId}/footage.mp4`;
|
|
4289
4273
|
}
|
|
4290
|
-
function resolveFontFile(envVar, bakedPath) {
|
|
4291
|
-
const explicit = process.env[envVar];
|
|
4292
|
-
if (explicit) {
|
|
4293
|
-
return explicit;
|
|
4294
|
-
}
|
|
4295
|
-
return existsSync5(bakedPath) ? bakedPath : undefined;
|
|
4296
|
-
}
|
|
4297
|
-
function resolveClipFontFile() {
|
|
4298
|
-
return resolveFontFile("CLIP_FONT_FILE", BOX_CLIP_FONT_FILE);
|
|
4299
|
-
}
|
|
4300
|
-
function resolveClipSansFontFile() {
|
|
4301
|
-
return resolveFontFile("CLIP_SANS_FONT_FILE", BOX_CLIP_SANS_FONT_FILE);
|
|
4302
|
-
}
|
|
4303
4274
|
function setVideoUrl(logId) {
|
|
4304
4275
|
return `${FOUND_BASE3}/${encodeURIComponent(logId)}/set.mp4`;
|
|
4305
4276
|
}
|
|
4306
|
-
function escapeDrawtextValue(text) {
|
|
4307
|
-
return text.replace(/\\/g, "\\\\").replace(/:/g, "\\:").replace(/,/g, "\\,").replace(/%/g, "\\%").replace(/'/g, "'\\''");
|
|
4308
|
-
}
|
|
4309
|
-
function brandDrawtext(options) {
|
|
4310
|
-
const value = escapeDrawtextValue(options.text);
|
|
4311
|
-
const font = options.fontFile ? `:fontfile='${options.fontFile}'` : "";
|
|
4312
|
-
const border = options.borderw && options.borderw > 0 ? `:borderw=${options.borderw}:bordercolor=${CLIP_HALO_COLOR}` : "";
|
|
4313
|
-
const enable = options.enable ? `:enable='${options.enable}'` : "";
|
|
4314
|
-
return `drawtext=text='${value}'${font}:` + `fontcolor=${options.color}:fontsize=${options.size}:` + `x=${options.x}:y=${options.y}${border}${enable}`;
|
|
4315
|
-
}
|
|
4316
|
-
function resolveTitleLines(options, y) {
|
|
4317
|
-
const inMs = options.inMs ?? 0;
|
|
4318
|
-
const outMs = options.outMs ?? 0;
|
|
4319
|
-
const clipDur = Math.max(0, (outMs - inMs) / 1000);
|
|
4320
|
-
const base = { fontFile: options.sansFontFile, size: CLIP_TITLE_SIZE, x: CLIP_MARGIN_X, y };
|
|
4321
|
-
const resolved = options.members && options.members.length > 0 ? resolveClipTracks({
|
|
4322
|
-
inMs,
|
|
4323
|
-
members: options.members,
|
|
4324
|
-
outMs,
|
|
4325
|
-
setDurationMs: options.setDurationMs ?? 0
|
|
4326
|
-
}) : [];
|
|
4327
|
-
if (resolved.length === 0) {
|
|
4328
|
-
return [{ ...base, text: options.title }];
|
|
4329
|
-
}
|
|
4330
|
-
const clamp = (seconds) => Math.min(Math.max(seconds, 0), clipDur);
|
|
4331
|
-
return resolved.map((track, index) => {
|
|
4332
|
-
const relStart = clamp((track.startMs - inMs) / 1000);
|
|
4333
|
-
const next = resolved[index + 1];
|
|
4334
|
-
const relEnd = next ? clamp((next.startMs - inMs) / 1000) : clipDur;
|
|
4335
|
-
return {
|
|
4336
|
-
...base,
|
|
4337
|
-
enable: `between(t,${relStart.toFixed(3)},${relEnd.toFixed(3)})`,
|
|
4338
|
-
text: track.label
|
|
4339
|
-
};
|
|
4340
|
-
});
|
|
4341
|
-
}
|
|
4342
4277
|
function clipCutFilterComplex(options) {
|
|
4343
4278
|
const xOffset = Math.max(0, Math.round(options.xOffset));
|
|
4344
|
-
|
|
4345
|
-
const logId = options.logId?.trim();
|
|
4346
|
-
const hasCoordinate = Boolean(logId);
|
|
4347
|
-
const coordinateBox = Math.round(CLIP_COORDINATE_SIZE * 1.18);
|
|
4348
|
-
const titleBottomOffset = hasCoordinate ? CLIP_SAFE_BOTTOM + coordinateBox + CLIP_LINE_GAP : CLIP_SAFE_BOTTOM;
|
|
4349
|
-
const titleY = `h-${titleBottomOffset}-th`;
|
|
4350
|
-
const titleLines = resolveTitleLines(options, titleY);
|
|
4351
|
-
const coordinate2 = hasCoordinate ? {
|
|
4352
|
-
fontFile: options.oxaniumFontFile,
|
|
4353
|
-
size: CLIP_COORDINATE_SIZE,
|
|
4354
|
-
text: `fluncle://${logId}`,
|
|
4355
|
-
x: CLIP_MARGIN_X,
|
|
4356
|
-
y: `h-${CLIP_SAFE_BOTTOM}-th`
|
|
4357
|
-
} : undefined;
|
|
4358
|
-
const haloGlyphs = [
|
|
4359
|
-
...titleLines.map((line) => brandDrawtext({ ...line, color: CLIP_HALO_COLOR })),
|
|
4360
|
-
...coordinate2 ? [brandDrawtext({ ...coordinate2, color: CLIP_HALO_COLOR })] : []
|
|
4361
|
-
].join(",");
|
|
4362
|
-
const sharpGlyphs = [
|
|
4363
|
-
...titleLines.map((line) => brandDrawtext({ ...line, borderw: CLIP_SHARP_BORDERW, color: CLIP_TITLE_COLOR })),
|
|
4364
|
-
...coordinate2 ? [
|
|
4365
|
-
brandDrawtext({
|
|
4366
|
-
...coordinate2,
|
|
4367
|
-
borderw: CLIP_SHARP_BORDERW,
|
|
4368
|
-
color: CLIP_COORDINATE_COLOR
|
|
4369
|
-
})
|
|
4370
|
-
] : []
|
|
4371
|
-
].join(",");
|
|
4372
|
-
return [
|
|
4373
|
-
`[0:v]${crop},setsar=1[base]`,
|
|
4374
|
-
`color=c=black@0:s=${CLIP_WIDTH}x${CLIP_HEIGHT},format=rgba,${haloGlyphs}[ink]`,
|
|
4375
|
-
`[ink]split[ink1][ink2]`,
|
|
4376
|
-
`[ink1]gblur=sigma=${CLIP_HALO_CORE_SIGMA}[core]`,
|
|
4377
|
-
`[ink2]gblur=sigma=${CLIP_HALO_FEATHER_SIGMA}[feather]`,
|
|
4378
|
-
`[base][feather]overlay=0:0[b1]`,
|
|
4379
|
-
`[b1][core]overlay=0:0[b2]`,
|
|
4380
|
-
`[b2]${sharpGlyphs}[out]`
|
|
4381
|
-
].join(";");
|
|
4279
|
+
return `[0:v]crop=ih*9/16:ih:${xOffset}:0,scale=${CLIP_WIDTH}:${CLIP_HEIGHT},setsar=1[out]`;
|
|
4382
4280
|
}
|
|
4383
4281
|
function clipCutFfmpegArgs(options) {
|
|
4384
4282
|
const inSeconds = (options.inMs / 1000).toFixed(3);
|
|
@@ -4442,15 +4340,7 @@ async function resolveClipSource(clip) {
|
|
|
4442
4340
|
throw new CliError2("recording_not_staged", `Recording ${clip.recordingId} has no staged set video`);
|
|
4443
4341
|
}
|
|
4444
4342
|
return {
|
|
4445
|
-
|
|
4446
|
-
members: recording.tracklist.map((cue) => ({
|
|
4447
|
-
artists: cue.artists,
|
|
4448
|
-
startMs: cue.startMs,
|
|
4449
|
-
title: cue.title
|
|
4450
|
-
})),
|
|
4451
|
-
setDurationMs: recording.durationMs ?? 0,
|
|
4452
|
-
setUrl: `${FOUND_BASE3}/${recording.r2Key.split("/").map(encodeURIComponent).join("/")}`,
|
|
4453
|
-
title: recording.title
|
|
4343
|
+
setUrl: `${FOUND_BASE3}/${recording.r2Key.split("/").map(encodeURIComponent).join("/")}`
|
|
4454
4344
|
};
|
|
4455
4345
|
}
|
|
4456
4346
|
if (!clip.mixtapeId) {
|
|
@@ -4464,13 +4354,7 @@ async function resolveClipSource(clip) {
|
|
|
4464
4354
|
if (!mixtape.setVideoAt) {
|
|
4465
4355
|
throw new CliError2("set_not_staged", `Mixtape ${mixtape.logId} has no staged set video — run \`distribute --set-video\` first`);
|
|
4466
4356
|
}
|
|
4467
|
-
return {
|
|
4468
|
-
logId: mixtape.logId,
|
|
4469
|
-
members: mixtape.members,
|
|
4470
|
-
setDurationMs: mixtape.durationMs ?? 0,
|
|
4471
|
-
setUrl: setVideoUrl(mixtape.logId),
|
|
4472
|
-
title: mixtape.title
|
|
4473
|
-
};
|
|
4357
|
+
return { setUrl: setVideoUrl(mixtape.logId) };
|
|
4474
4358
|
}
|
|
4475
4359
|
async function clipCutCommand(clipId, onProgress = () => {}) {
|
|
4476
4360
|
const clips = await clipsListCommand();
|
|
@@ -4482,18 +4366,12 @@ async function clipCutCommand(clipId, onProgress = () => {}) {
|
|
|
4482
4366
|
await assertFfmpeg2();
|
|
4483
4367
|
const outputPath = join5(tmpdir2(), `fluncle-clip-${randomUUID2()}.mp4`);
|
|
4484
4368
|
try {
|
|
4485
|
-
onProgress(`Clip ${clipId}: cutting [${clip.inMs}–${clip.outMs}ms]
|
|
4369
|
+
onProgress(`Clip ${clipId}: cutting [${clip.inMs}–${clip.outMs}ms]…`);
|
|
4486
4370
|
await runClipCut({
|
|
4487
4371
|
inMs: clip.inMs,
|
|
4488
|
-
logId: source.logId,
|
|
4489
|
-
members: source.members,
|
|
4490
4372
|
outMs: clip.outMs,
|
|
4491
4373
|
outputPath,
|
|
4492
|
-
oxaniumFontFile: resolveClipFontFile(),
|
|
4493
|
-
sansFontFile: resolveClipSansFontFile(),
|
|
4494
|
-
setDurationMs: source.setDurationMs,
|
|
4495
4374
|
setUrl: source.setUrl,
|
|
4496
|
-
title: source.title,
|
|
4497
4375
|
xOffset: clip.xOffset
|
|
4498
4376
|
});
|
|
4499
4377
|
const sizeBytes = statSync4(outputPath).size;
|
|
@@ -4508,7 +4386,6 @@ async function clipCutCommand(clipId, onProgress = () => {}) {
|
|
|
4508
4386
|
return {
|
|
4509
4387
|
clipId,
|
|
4510
4388
|
key: presign.key,
|
|
4511
|
-
logId: source.logId,
|
|
4512
4389
|
sizeBytes,
|
|
4513
4390
|
url: `${FOUND_BASE3}/${presign.key}`
|
|
4514
4391
|
};
|
|
@@ -4552,9 +4429,8 @@ async function runClipCut(options) {
|
|
|
4552
4429
|
throw new CliError2("ffmpeg_failed", `ffmpeg failed to cut the clip${detail ? `: ${detail}` : ""}`);
|
|
4553
4430
|
}
|
|
4554
4431
|
}
|
|
4555
|
-
var FOUND_BASE3 = "https://found.fluncle.com", CLIP_WIDTH = 1080, CLIP_HEIGHT = 1920, CLIP_CRF = 21, CLIP_MAXRATE = "10M", CLIP_BUFSIZE = "20M", CLIP_AUDIO_BITRATE = "192k", MAX_CLIP_BYTES
|
|
4432
|
+
var FOUND_BASE3 = "https://found.fluncle.com", CLIP_WIDTH = 1080, CLIP_HEIGHT = 1920, CLIP_CRF = 21, CLIP_MAXRATE = "10M", CLIP_BUFSIZE = "20M", CLIP_AUDIO_BITRATE = "192k", MAX_CLIP_BYTES;
|
|
4556
4433
|
var init_clips = __esm(() => {
|
|
4557
|
-
init_util();
|
|
4558
4434
|
init_api();
|
|
4559
4435
|
init_output();
|
|
4560
4436
|
MAX_CLIP_BYTES = 100 * 1024 * 1024;
|
|
@@ -4571,7 +4447,7 @@ __export(exports_recordings2, {
|
|
|
4571
4447
|
recordingDeleteCommand: () => recordingDeleteCommand2,
|
|
4572
4448
|
recordingCreateCommand: () => recordingCreateCommand2
|
|
4573
4449
|
});
|
|
4574
|
-
import { existsSync as
|
|
4450
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
|
|
4575
4451
|
async function recordingGet2(id) {
|
|
4576
4452
|
const response = await adminApiGet(`/api/admin/recordings/${encodeURIComponent(id)}`);
|
|
4577
4453
|
return response.recording;
|
|
@@ -4618,7 +4494,7 @@ async function recordingCreateCommand2(options = {}) {
|
|
|
4618
4494
|
if (!options.video) {
|
|
4619
4495
|
throw new CliError2("missing_video", "A recording needs a --video <file> to stage");
|
|
4620
4496
|
}
|
|
4621
|
-
if (!
|
|
4497
|
+
if (!existsSync5(options.video)) {
|
|
4622
4498
|
throw new CliError2("file_not_found", `Set-video master not found: ${options.video}`);
|
|
4623
4499
|
}
|
|
4624
4500
|
const created = await adminApiPost("/api/admin/recordings", {
|
|
@@ -4651,7 +4527,7 @@ async function recordingUpdateCommand2(id, options = {}) {
|
|
|
4651
4527
|
body.recordedAt = options.recordedAt;
|
|
4652
4528
|
}
|
|
4653
4529
|
if (options.tracklistFile !== undefined) {
|
|
4654
|
-
if (!
|
|
4530
|
+
if (!existsSync5(options.tracklistFile)) {
|
|
4655
4531
|
throw new CliError2("file_not_found", `Tracklist file not found: ${options.tracklistFile}`);
|
|
4656
4532
|
}
|
|
4657
4533
|
try {
|
|
@@ -4708,7 +4584,7 @@ __export(exports_newsletter, {
|
|
|
4708
4584
|
newsletterDraftCommand: () => newsletterDraftCommand,
|
|
4709
4585
|
newsletterDeleteCommand: () => newsletterDeleteCommand
|
|
4710
4586
|
});
|
|
4711
|
-
import { existsSync as
|
|
4587
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
|
|
4712
4588
|
function buildBody3(options, { requireContent }) {
|
|
4713
4589
|
const body = {};
|
|
4714
4590
|
if (options.contentFile !== undefined) {
|
|
@@ -4728,7 +4604,7 @@ function buildBody3(options, { requireContent }) {
|
|
|
4728
4604
|
return body;
|
|
4729
4605
|
}
|
|
4730
4606
|
function readContentFile(filePath) {
|
|
4731
|
-
if (!
|
|
4607
|
+
if (!existsSync6(filePath)) {
|
|
4732
4608
|
throw new CliError2("file_not_found", `Content file not found: ${filePath}`);
|
|
4733
4609
|
}
|
|
4734
4610
|
const text = readFileSync6(filePath, "utf-8");
|
|
@@ -5366,7 +5242,7 @@ var init_format2 = __esm(() => {
|
|
|
5366
5242
|
});
|
|
5367
5243
|
|
|
5368
5244
|
// src/cli.ts
|
|
5369
|
-
import { existsSync as
|
|
5245
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7 } from "fs";
|
|
5370
5246
|
import path2 from "path";
|
|
5371
5247
|
|
|
5372
5248
|
// ../../node_modules/commander/lib/error.js
|
|
@@ -7612,6 +7488,10 @@ function addAdminCommands(program2) {
|
|
|
7612
7488
|
const { queueCommand: queueCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
7613
7489
|
await runAdminQueue(options, queueCommand2);
|
|
7614
7490
|
});
|
|
7491
|
+
adminTracks.command("list").description("List findings, filterable by musical-key presence (--no-key / --has-key)").option("--limit <limit>", "Number of findings to show", "50").option("--no-key", "Only findings with NO stored musical key (the key-backfill backlog)").option("--has-key <bool>", "Filter by key presence: true (has key) or false (missing)").option("--order <order>", "Sort: asc (oldest first) or desc (newest first)", "desc").option("--json", "Print JSON", false).action(async (options) => {
|
|
7492
|
+
const { listCommand: listCommand2 } = await Promise.resolve().then(() => (init_admin_tracks(), exports_admin_tracks));
|
|
7493
|
+
await runAdminTracksList(options, listCommand2);
|
|
7494
|
+
});
|
|
7615
7495
|
adminTracks.command("enrich").description("Enrichment worklist (pending, failed, or stuck processing) \u2014 use --queue").option("--queue", "Show the enrichment worklist, oldest first", false).option("--limit <limit>", "Number of findings to show with --queue", "10").option("--json", "Print JSON", false).action(async (options) => {
|
|
7616
7496
|
if (!options.queue) {
|
|
7617
7497
|
console.error("`tracks enrich` is a worklist view \u2014 enrichment runs on the on-box `fluncle-enrich` cron.\nUse `tracks enrich --queue` to see findings needing (re-)enrichment.");
|
|
@@ -8075,7 +7955,7 @@ async function runTrackVideo(idOrLogId, options, trackVideoCommand2) {
|
|
|
8075
7955
|
return;
|
|
8076
7956
|
}
|
|
8077
7957
|
const candidate = path2.join(dir, name);
|
|
8078
|
-
return
|
|
7958
|
+
return existsSync7(candidate) ? candidate : undefined;
|
|
8079
7959
|
};
|
|
8080
7960
|
const resolveFile = (explicit, name) => {
|
|
8081
7961
|
if (explicit) {
|
|
@@ -8566,6 +8446,39 @@ function parseListLimit(value) {
|
|
|
8566
8446
|
}
|
|
8567
8447
|
return limit;
|
|
8568
8448
|
}
|
|
8449
|
+
function resolveHasKey(options) {
|
|
8450
|
+
if (options.key === false) {
|
|
8451
|
+
return false;
|
|
8452
|
+
}
|
|
8453
|
+
if (options.hasKey === "false") {
|
|
8454
|
+
return false;
|
|
8455
|
+
}
|
|
8456
|
+
if (options.hasKey === "true") {
|
|
8457
|
+
return true;
|
|
8458
|
+
}
|
|
8459
|
+
return;
|
|
8460
|
+
}
|
|
8461
|
+
async function runAdminTracksList(options, listCommand2) {
|
|
8462
|
+
const limit = parseListLimit(options.limit);
|
|
8463
|
+
const order = options.order === "asc" ? "asc" : "desc";
|
|
8464
|
+
const hasKey = resolveHasKey(options);
|
|
8465
|
+
const tracks = await listCommand2({ hasKey, limit, order });
|
|
8466
|
+
if (options.json) {
|
|
8467
|
+
printJson({ ok: true, tracks });
|
|
8468
|
+
return;
|
|
8469
|
+
}
|
|
8470
|
+
if (tracks.length === 0) {
|
|
8471
|
+
const scope2 = hasKey === false ? " missing a key" : hasKey === true ? " with a key" : "";
|
|
8472
|
+
console.log(`No findings${scope2}.`);
|
|
8473
|
+
return;
|
|
8474
|
+
}
|
|
8475
|
+
const { trackRows: trackRows2 } = await Promise.resolve().then(() => (init_format2(), exports_format));
|
|
8476
|
+
const scope = hasKey === false ? " missing a musical key" : hasKey === true ? " with a musical key" : "";
|
|
8477
|
+
const noun = tracks.length === 1 ? "finding" : "findings";
|
|
8478
|
+
console.log(`${tracks.length} ${noun}${scope}:`);
|
|
8479
|
+
console.log(trackRows2(tracks).join(`
|
|
8480
|
+
`));
|
|
8481
|
+
}
|
|
8569
8482
|
async function runAdminQueue(options, queueCommand2) {
|
|
8570
8483
|
const limit = parseListLimit(options.limit);
|
|
8571
8484
|
const tracks = await queueCommand2(limit, {
|
package/package.json
CHANGED