fluncle 0.74.0 → 0.75.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 +89 -21
- package/package.json +1 -1
package/bin/fluncle.mjs
CHANGED
|
@@ -524,7 +524,7 @@ function parseVersion(version) {
|
|
|
524
524
|
var currentVersion;
|
|
525
525
|
var init_version = __esm(() => {
|
|
526
526
|
init_output();
|
|
527
|
-
currentVersion = "0.
|
|
527
|
+
currentVersion = "0.75.0".trim() ? "0.75.0".trim() : "0.1.0";
|
|
528
528
|
});
|
|
529
529
|
|
|
530
530
|
// src/update-notifier.ts
|
|
@@ -2381,7 +2381,7 @@ function parseVersion2(version) {
|
|
|
2381
2381
|
var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
|
|
2382
2382
|
var init_version2 = __esm(() => {
|
|
2383
2383
|
init_output();
|
|
2384
|
-
currentVersion2 = "0.
|
|
2384
|
+
currentVersion2 = "0.75.0".trim() ? "0.75.0".trim() : "0.1.0";
|
|
2385
2385
|
});
|
|
2386
2386
|
|
|
2387
2387
|
// ../../packages/registry/src/index.ts
|
|
@@ -3001,7 +3001,7 @@ var init_src = __esm(() => {
|
|
|
3001
3001
|
],
|
|
3002
3002
|
kind: "cron",
|
|
3003
3003
|
name: "cron.healthcheck",
|
|
3004
|
-
operatorNotes: "every 10m. Pure probing, zero LLM tokens. POSTs to the agent-tier record_health op that /status reads.",
|
|
3004
|
+
operatorNotes: "every 10m, run by a rave-02 host systemd timer (docs/agents/hermes/healthcheck-timer/) — decoupled from the Hermes cron gateway so the prober isn't starved by the scheduler it monitors. Pure probing, zero LLM tokens. POSTs to the agent-tier record_health op that /status reads.",
|
|
3005
3005
|
probeConfig: { cadenceMs: 10 * MINUTE_MS, cronName: "fluncle-healthcheck", kind: "cron" },
|
|
3006
3006
|
weights: { status: "hidden" }
|
|
3007
3007
|
},
|
|
@@ -3975,47 +3975,110 @@ var init_mixtapes2 = __esm(() => {
|
|
|
3975
3975
|
var exports_clips = {};
|
|
3976
3976
|
__export(exports_clips, {
|
|
3977
3977
|
setVideoUrl: () => setVideoUrl,
|
|
3978
|
+
resolveClipSansFontFile: () => resolveClipSansFontFile,
|
|
3979
|
+
resolveClipFontFile: () => resolveClipFontFile,
|
|
3978
3980
|
escapeDrawtextValue: () => escapeDrawtextValue,
|
|
3979
3981
|
clipsListCommand: () => clipsListCommand,
|
|
3980
3982
|
clipFootageKey: () => clipFootageKey,
|
|
3981
|
-
|
|
3983
|
+
clipCutFilterComplex: () => clipCutFilterComplex,
|
|
3982
3984
|
clipCutFfmpegArgs: () => clipCutFfmpegArgs,
|
|
3983
3985
|
clipCutCommand: () => clipCutCommand,
|
|
3986
|
+
brandDrawtext: () => brandDrawtext,
|
|
3984
3987
|
MAX_CLIP_BYTES: () => MAX_CLIP_BYTES,
|
|
3985
3988
|
CLIP_WIDTH: () => CLIP_WIDTH,
|
|
3989
|
+
CLIP_TITLE_SIZE: () => CLIP_TITLE_SIZE,
|
|
3990
|
+
CLIP_TITLE_COLOR: () => CLIP_TITLE_COLOR,
|
|
3991
|
+
CLIP_SHARP_BORDERW: () => CLIP_SHARP_BORDERW,
|
|
3992
|
+
CLIP_SAFE_BOTTOM: () => CLIP_SAFE_BOTTOM,
|
|
3986
3993
|
CLIP_MAXRATE: () => CLIP_MAXRATE,
|
|
3994
|
+
CLIP_MARGIN_X: () => CLIP_MARGIN_X,
|
|
3995
|
+
CLIP_LINE_GAP: () => CLIP_LINE_GAP,
|
|
3987
3996
|
CLIP_HEIGHT: () => CLIP_HEIGHT,
|
|
3997
|
+
CLIP_HALO_FEATHER_SIGMA: () => CLIP_HALO_FEATHER_SIGMA,
|
|
3998
|
+
CLIP_HALO_CORE_SIGMA: () => CLIP_HALO_CORE_SIGMA,
|
|
3999
|
+
CLIP_HALO_COLOR: () => CLIP_HALO_COLOR,
|
|
3988
4000
|
CLIP_CRF: () => CLIP_CRF,
|
|
4001
|
+
CLIP_COORDINATE_SIZE: () => CLIP_COORDINATE_SIZE,
|
|
4002
|
+
CLIP_COORDINATE_COLOR: () => CLIP_COORDINATE_COLOR,
|
|
3989
4003
|
CLIP_BUFSIZE: () => CLIP_BUFSIZE,
|
|
3990
|
-
CLIP_AUDIO_BITRATE: () => CLIP_AUDIO_BITRATE
|
|
4004
|
+
CLIP_AUDIO_BITRATE: () => CLIP_AUDIO_BITRATE,
|
|
4005
|
+
BOX_CLIP_SANS_FONT_FILE: () => BOX_CLIP_SANS_FONT_FILE,
|
|
4006
|
+
BOX_CLIP_FONT_FILE: () => BOX_CLIP_FONT_FILE
|
|
3991
4007
|
});
|
|
3992
4008
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
3993
|
-
import { rmSync as rmSync3, statSync as statSync4 } from "node:fs";
|
|
4009
|
+
import { existsSync as existsSync4, rmSync as rmSync3, statSync as statSync4 } from "node:fs";
|
|
3994
4010
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
3995
4011
|
import { join as join5 } from "node:path";
|
|
3996
4012
|
function clipFootageKey(clipId) {
|
|
3997
4013
|
return `${clipId}/footage.mp4`;
|
|
3998
4014
|
}
|
|
4015
|
+
function resolveFontFile(envVar, bakedPath) {
|
|
4016
|
+
const explicit = process.env[envVar];
|
|
4017
|
+
if (explicit) {
|
|
4018
|
+
return explicit;
|
|
4019
|
+
}
|
|
4020
|
+
return existsSync4(bakedPath) ? bakedPath : undefined;
|
|
4021
|
+
}
|
|
4022
|
+
function resolveClipFontFile() {
|
|
4023
|
+
return resolveFontFile("CLIP_FONT_FILE", BOX_CLIP_FONT_FILE);
|
|
4024
|
+
}
|
|
4025
|
+
function resolveClipSansFontFile() {
|
|
4026
|
+
return resolveFontFile("CLIP_SANS_FONT_FILE", BOX_CLIP_SANS_FONT_FILE);
|
|
4027
|
+
}
|
|
3999
4028
|
function setVideoUrl(logId) {
|
|
4000
4029
|
return `${FOUND_BASE3}/${encodeURIComponent(logId)}/set.mp4`;
|
|
4001
4030
|
}
|
|
4002
4031
|
function escapeDrawtextValue(text) {
|
|
4003
4032
|
return text.replace(/\\/g, "\\\\").replace(/:/g, "\\:").replace(/,/g, "\\,").replace(/%/g, "\\%").replace(/'/g, "'\\''");
|
|
4004
4033
|
}
|
|
4005
|
-
function
|
|
4034
|
+
function brandDrawtext(options) {
|
|
4035
|
+
const value = escapeDrawtextValue(options.text);
|
|
4036
|
+
const font = options.fontFile ? `:fontfile='${options.fontFile}'` : "";
|
|
4037
|
+
const border = options.borderw && options.borderw > 0 ? `:borderw=${options.borderw}:bordercolor=${CLIP_HALO_COLOR}` : "";
|
|
4038
|
+
return `drawtext=text='${value}'${font}:` + `fontcolor=${options.color}:fontsize=${options.size}:` + `x=${options.x}:y=${options.y}${border}`;
|
|
4039
|
+
}
|
|
4040
|
+
function clipCutFilterComplex(options) {
|
|
4006
4041
|
const xOffset = Math.max(0, Math.round(options.xOffset));
|
|
4007
4042
|
const crop = `crop=ih*9/16:ih:${xOffset}:0,scale=${CLIP_WIDTH}:${CLIP_HEIGHT}`;
|
|
4008
|
-
const
|
|
4009
|
-
const
|
|
4010
|
-
const
|
|
4011
|
-
|
|
4012
|
-
|
|
4013
|
-
|
|
4043
|
+
const coordinateBox = Math.round(CLIP_COORDINATE_SIZE * 1.18);
|
|
4044
|
+
const titleBottomOffset = CLIP_SAFE_BOTTOM + coordinateBox + CLIP_LINE_GAP;
|
|
4045
|
+
const title = {
|
|
4046
|
+
fontFile: options.sansFontFile,
|
|
4047
|
+
size: CLIP_TITLE_SIZE,
|
|
4048
|
+
text: options.title,
|
|
4049
|
+
x: CLIP_MARGIN_X,
|
|
4050
|
+
y: `h-${titleBottomOffset}-th`
|
|
4051
|
+
};
|
|
4052
|
+
const coordinate2 = {
|
|
4053
|
+
fontFile: options.oxaniumFontFile,
|
|
4054
|
+
size: CLIP_COORDINATE_SIZE,
|
|
4055
|
+
text: `fluncle://${options.logId}`,
|
|
4056
|
+
x: CLIP_MARGIN_X,
|
|
4057
|
+
y: `h-${CLIP_SAFE_BOTTOM}-th`
|
|
4058
|
+
};
|
|
4059
|
+
const haloGlyphs = [
|
|
4060
|
+
brandDrawtext({ ...title, color: CLIP_HALO_COLOR }),
|
|
4061
|
+
brandDrawtext({ ...coordinate2, color: CLIP_HALO_COLOR })
|
|
4062
|
+
].join(",");
|
|
4063
|
+
const sharpGlyphs = [
|
|
4064
|
+
brandDrawtext({ ...title, borderw: CLIP_SHARP_BORDERW, color: CLIP_TITLE_COLOR }),
|
|
4065
|
+
brandDrawtext({ ...coordinate2, borderw: CLIP_SHARP_BORDERW, color: CLIP_COORDINATE_COLOR })
|
|
4066
|
+
].join(",");
|
|
4067
|
+
return [
|
|
4068
|
+
`[0:v]${crop},setsar=1[base]`,
|
|
4069
|
+
`color=c=black@0:s=${CLIP_WIDTH}x${CLIP_HEIGHT},format=rgba,${haloGlyphs}[ink]`,
|
|
4070
|
+
`[ink]split[ink1][ink2]`,
|
|
4071
|
+
`[ink1]gblur=sigma=${CLIP_HALO_CORE_SIGMA}[core]`,
|
|
4072
|
+
`[ink2]gblur=sigma=${CLIP_HALO_FEATHER_SIGMA}[feather]`,
|
|
4073
|
+
`[base][feather]overlay=0:0[b1]`,
|
|
4074
|
+
`[b1][core]overlay=0:0[b2]`,
|
|
4075
|
+
`[b2]${sharpGlyphs}[out]`
|
|
4076
|
+
].join(";");
|
|
4014
4077
|
}
|
|
4015
4078
|
function clipCutFfmpegArgs(options) {
|
|
4016
4079
|
const inSeconds = (options.inMs / 1000).toFixed(3);
|
|
4017
4080
|
const durationSeconds = ((options.outMs - options.inMs) / 1000).toFixed(3);
|
|
4018
|
-
const filter =
|
|
4081
|
+
const filter = clipCutFilterComplex(options);
|
|
4019
4082
|
return [
|
|
4020
4083
|
"-y",
|
|
4021
4084
|
"-ss",
|
|
@@ -4024,8 +4087,12 @@ function clipCutFfmpegArgs(options) {
|
|
|
4024
4087
|
options.setUrl,
|
|
4025
4088
|
"-t",
|
|
4026
4089
|
durationSeconds,
|
|
4027
|
-
"-
|
|
4090
|
+
"-filter_complex",
|
|
4028
4091
|
filter,
|
|
4092
|
+
"-map",
|
|
4093
|
+
"[out]",
|
|
4094
|
+
"-map",
|
|
4095
|
+
"0:a?",
|
|
4029
4096
|
"-c:v",
|
|
4030
4097
|
"libx264",
|
|
4031
4098
|
"-preset",
|
|
@@ -4079,11 +4146,12 @@ async function clipCutCommand(clipId, onProgress = () => {}) {
|
|
|
4079
4146
|
try {
|
|
4080
4147
|
onProgress(`Clip ${clipId}: cutting [${clip.inMs}–${clip.outMs}ms] from ${mixtape.logId}…`);
|
|
4081
4148
|
await runClipCut({
|
|
4082
|
-
fontFile: process.env.CLIP_FONT_FILE,
|
|
4083
4149
|
inMs: clip.inMs,
|
|
4084
4150
|
logId: mixtape.logId,
|
|
4085
4151
|
outMs: clip.outMs,
|
|
4086
4152
|
outputPath,
|
|
4153
|
+
oxaniumFontFile: resolveClipFontFile(),
|
|
4154
|
+
sansFontFile: resolveClipSansFontFile(),
|
|
4087
4155
|
setUrl,
|
|
4088
4156
|
title: mixtape.title,
|
|
4089
4157
|
xOffset: clip.xOffset
|
|
@@ -4144,7 +4212,7 @@ async function runClipCut(options) {
|
|
|
4144
4212
|
throw new CliError2("ffmpeg_failed", `ffmpeg failed to cut the clip${detail ? `: ${detail}` : ""}`);
|
|
4145
4213
|
}
|
|
4146
4214
|
}
|
|
4147
|
-
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;
|
|
4215
|
+
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, CLIP_TITLE_COLOR = "0xf4ead7", CLIP_COORDINATE_COLOR = "0xb7ab95", CLIP_HALO_COLOR = "0x090a0b", CLIP_HALO_CORE_SIGMA = 3, CLIP_HALO_FEATHER_SIGMA = 9, CLIP_SHARP_BORDERW = 1, CLIP_MARGIN_X = 96, CLIP_SAFE_BOTTOM = 230, CLIP_LINE_GAP = 10, CLIP_TITLE_SIZE = 40, CLIP_COORDINATE_SIZE = 22, BOX_CLIP_FONT_FILE = "/opt/fonts/Oxanium-SemiBold.ttf", BOX_CLIP_SANS_FONT_FILE = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf";
|
|
4148
4216
|
var init_clips = __esm(() => {
|
|
4149
4217
|
init_api();
|
|
4150
4218
|
init_output();
|
|
@@ -4160,7 +4228,7 @@ __export(exports_newsletter, {
|
|
|
4160
4228
|
newsletterDraftCommand: () => newsletterDraftCommand,
|
|
4161
4229
|
newsletterDeleteCommand: () => newsletterDeleteCommand
|
|
4162
4230
|
});
|
|
4163
|
-
import { existsSync as
|
|
4231
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
|
|
4164
4232
|
function buildBody3(options, { requireContent }) {
|
|
4165
4233
|
const body = {};
|
|
4166
4234
|
if (options.contentFile !== undefined) {
|
|
@@ -4180,7 +4248,7 @@ function buildBody3(options, { requireContent }) {
|
|
|
4180
4248
|
return body;
|
|
4181
4249
|
}
|
|
4182
4250
|
function readContentFile(filePath) {
|
|
4183
|
-
if (!
|
|
4251
|
+
if (!existsSync5(filePath)) {
|
|
4184
4252
|
throw new CliError2("file_not_found", `Content file not found: ${filePath}`);
|
|
4185
4253
|
}
|
|
4186
4254
|
const text = readFileSync4(filePath, "utf-8");
|
|
@@ -4822,7 +4890,7 @@ var init_format2 = __esm(() => {
|
|
|
4822
4890
|
});
|
|
4823
4891
|
|
|
4824
4892
|
// src/cli.ts
|
|
4825
|
-
import { existsSync as
|
|
4893
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
|
|
4826
4894
|
import path2 from "path";
|
|
4827
4895
|
|
|
4828
4896
|
// ../../node_modules/commander/lib/error.js
|
|
@@ -7499,7 +7567,7 @@ async function runTrackVideo(idOrLogId, options, trackVideoCommand2) {
|
|
|
7499
7567
|
return;
|
|
7500
7568
|
}
|
|
7501
7569
|
const candidate = path2.join(dir, name);
|
|
7502
|
-
return
|
|
7570
|
+
return existsSync6(candidate) ? candidate : undefined;
|
|
7503
7571
|
};
|
|
7504
7572
|
const resolveFile = (explicit, name) => {
|
|
7505
7573
|
if (explicit) {
|
package/package.json
CHANGED