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.
Files changed (2) hide show
  1. package/bin/fluncle.mjs +89 -21
  2. 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.74.0".trim() ? "0.74.0".trim() : "0.1.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.74.0".trim() ? "0.74.0".trim() : "0.1.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
- clipCutVideoFilter: () => clipCutVideoFilter,
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 clipCutVideoFilter(options) {
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 font = options.fontFile ? `:fontfile='${options.fontFile}'` : "";
4009
- const title = escapeDrawtextValue(options.title);
4010
- const coordinate2 = escapeDrawtextValue(`fluncle://${options.logId}`);
4011
- const titleDraw = `drawtext=text='${title}'${font}:fontcolor=white:fontsize=46:` + `x=56:y=h-208:box=1:boxcolor=black@0.55:boxborderw=18`;
4012
- const coordinateDraw = `drawtext=text='${coordinate2}'${font}:fontcolor=0xF4EAD7:fontsize=30:` + `x=56:y=h-132:box=1:boxcolor=black@0.55:boxborderw=12`;
4013
- return `${crop},${titleDraw},${coordinateDraw}`;
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 = clipCutVideoFilter(options);
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
- "-vf",
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 existsSync4, readFileSync as readFileSync4 } from "node:fs";
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 (!existsSync4(filePath)) {
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 existsSync5, readFileSync as readFileSync5 } from "fs";
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 existsSync5(candidate) ? candidate : undefined;
7570
+ return existsSync6(candidate) ? candidate : undefined;
7503
7571
  };
7504
7572
  const resolveFile = (explicit, name) => {
7505
7573
  if (explicit) {
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.74.0"
34
+ "version": "0.75.0"
35
35
  }