fix(growing): read SMB params from env at mount time, not module load

Root cause of growing producing .mov instead of XDCAM HD422 .mxf:

mountGrowingShare() used module-level consts (GROWING_SMB_MOUNT etc.)
captured from process.env at IMPORT time. Standby capture containers boot
with these unset and receive the SMB mount/credentials per-session over
/capture/start (capture.js sets process.env right before start()). Because
the consts were already frozen empty, mountGrowingShare() saw no mount
source, returned false, and growing silently fell back to S3 streaming —
producing an HEVC .mov while the asset key said .mxf.

Fix: growingSmbConfig() reads process.env fresh at mount time. Also drop
the stale const guard in unmountGrowingShare().
This commit is contained in:
Zac Gaetano 2026-06-04 12:51:32 +00:00
parent cb25711ec6
commit ac1d7e1e1f

View file

@ -39,10 +39,18 @@ function toUncShare(raw) {
if (!s.startsWith('//')) s = '//' + s.replace(/^\/+/, ''); // host/share -> //host/share if (!s.startsWith('//')) s = '//' + s.replace(/^\/+/, ''); // host/share -> //host/share
return s; return s;
} }
const GROWING_SMB_MOUNT = toUncShare(process.env.GROWING_SMB_MOUNT || ''); // Growing SMB params are read FRESH from process.env at mount time, NOT cached
const GROWING_SMB_USERNAME = process.env.GROWING_SMB_USERNAME || ''; // at module load. Standby capture containers boot with these unset and receive
const GROWING_SMB_PASSWORD = process.env.GROWING_SMB_PASSWORD || ''; // them per-session over /capture/start (capture.js sets process.env before
const GROWING_SMB_VERS = process.env.GROWING_SMB_VERS || '3.0'; // captureManager.start()). Caching them in module-level consts at import time
// captured the empty boot values, so the mount silently no-op'd and growing
// fell back to S3 — producing .mov instead of the XDCAM HD422 .mxf.
const growingSmbConfig = () => ({
mount: toUncShare(process.env.GROWING_SMB_MOUNT || ''),
username: process.env.GROWING_SMB_USERNAME || '',
password: process.env.GROWING_SMB_PASSWORD || '',
vers: process.env.GROWING_SMB_VERS || '3.0',
});
const SMB_CREDS_FILE = '/run/smb-creds'; const SMB_CREDS_FILE = '/run/smb-creds';
// True when GROWING_PATH is already a mountpoint (e.g. a prior session left it // True when GROWING_PATH is already a mountpoint (e.g. a prior session left it
@ -57,7 +65,11 @@ function isMounted(path) {
// Returns true on success (or if already mounted), false on failure — callers // Returns true on success (or if already mounted), false on failure — callers
// fall back to S3 streaming so a recording is never lost. // fall back to S3 streaming so a recording is never lost.
function mountGrowingShare() { function mountGrowingShare() {
if (!GROWING_SMB_MOUNT) return false; const cfg = growingSmbConfig();
if (!cfg.mount) {
console.warn('[capture] growing requested but GROWING_SMB_MOUNT is empty — falling back to S3');
return false;
}
try { try {
if (isMounted(GROWING_PATH)) { if (isMounted(GROWING_PATH)) {
console.log('[capture] growing share already mounted at', GROWING_PATH); console.log('[capture] growing share already mounted at', GROWING_PATH);
@ -70,14 +82,14 @@ function mountGrowingShare() {
// username/password mount inline and smbclient lists the share fine. Inline // username/password mount inline and smbclient lists the share fine. Inline
// user=/password= is the reliable form here. // user=/password= is the reliable form here.
const opts = [ const opts = [
`username=${GROWING_SMB_USERNAME}`, `username=${cfg.username}`,
`password=${GROWING_SMB_PASSWORD}`, `password=${cfg.password}`,
'uid=0', 'gid=0', 'file_mode=0664', 'dir_mode=0775', 'uid=0', 'gid=0', 'file_mode=0664', 'dir_mode=0775',
`vers=${GROWING_SMB_VERS}`, `vers=${cfg.vers}`,
].join(','); ].join(',');
execFileSync('mount', ['-t', 'cifs', GROWING_SMB_MOUNT, GROWING_PATH, '-o', opts], execFileSync('mount', ['-t', 'cifs', cfg.mount, GROWING_PATH, '-o', opts],
{ stdio: ['ignore', 'ignore', 'pipe'] }); { stdio: ['ignore', 'ignore', 'pipe'] });
console.log('[capture] mounted CIFS growing share', GROWING_SMB_MOUNT, '->', GROWING_PATH); console.log('[capture] mounted CIFS growing share', cfg.mount, '->', GROWING_PATH);
return true; return true;
} catch (err) { } catch (err) {
const stderr = err.stderr ? err.stderr.toString().trim() : err.message; const stderr = err.stderr ? err.stderr.toString().trim() : err.message;
@ -88,7 +100,6 @@ function mountGrowingShare() {
// Best-effort unmount on session stop. Ignores "not mounted". // Best-effort unmount on session stop. Ignores "not mounted".
function unmountGrowingShare() { function unmountGrowingShare() {
if (!GROWING_SMB_MOUNT) return;
try { try {
if (isMounted(GROWING_PATH)) { if (isMounted(GROWING_PATH)) {
execFileSync('umount', [GROWING_PATH], { stdio: ['ignore', 'ignore', 'pipe'] }); execFileSync('umount', [GROWING_PATH], { stdio: ['ignore', 'ignore', 'pipe'] });