Discord Stream Recording Injection
Created on 19 Feb 2026

thread link: uwu
Record incoming Discord direct video stream frames to local MP4 files:
~/discord-recs<unix>-<5rand>-stream<streamId>.mp4/Users/soham/Library/Application Support/discord/0.0.377/modules/discord_voice/index.jsdiscord_modules_0.0.377/modules/discord_voice/index.jsUse this ffmpeg path inside the injection:
/Users/soham/.nix-profile/bin/ffmpegPaste these blocks into discord_voice/index.js.
const childProcess = require('child_process');
Error.stackTraceLimit = 100;
videoStreams / directVideoStreams)const activeRecorders = new Map();
function randomToken(length) {
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
let out = '';
for (let i = 0; i < length; i++) out += chars[Math.floor(Math.random() * chars.length)];
return out;
}
function getRecordingDir() {
return process.env.DISCORD_REC_DIR || path.join(os.homedir(), 'discord-recs');
}
function getRecordingPath(streamId) {
const ts = Math.floor(Date.now() / 1000);
const token = randomToken(5);
const ext = process.env.DISCORD_REC_EXT || 'mp4';
return path.join(getRecordingDir(), `${ts}-${token}-stream${streamId}.${ext}`);
}
function waitForDrain(stream) {
return new Promise(resolve => stream.once('drain', resolve));
}
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function startRecorderProcess(state, width, height) {
const outputPath = getRecordingPath(state.streamId);
const ffmpegBin = process.env.DISCORD_REC_FFMPEG || '/Users/soham/.nix-profile/bin/ffmpeg';
const fps = String(process.env.DISCORD_REC_FPS || 30);
const args = [
'-y',
'-f', 'rawvideo',
'-pix_fmt', 'rgba',
'-s', `${width}x${height}`,
'-r', fps,
'-i', '-',
'-an',
'-c:v', 'libx264',
'-preset', 'veryfast',
'-crf', '20',
'-pix_fmt', 'yuv420p',
outputPath,
];
state.outputPath = outputPath;
state.width = width;
state.height = height;
state.ffmpeg = childProcess.spawn(ffmpegBin, args, { stdio: ['pipe', 'ignore', 'pipe'] });
state.ffmpeg.on('error', err => {
trace('recorder_ffmpeg_error', {
streamId: state.streamId,
ffmpegBin,
error: err?.message ?? String(err),
});
});
state.ffmpeg.stderr.on('data', chunk => {
trace('recorder_ffmpeg_stderr', {
streamId: state.streamId,
stderr: String(chunk),
});
});
state.ffmpeg.on('exit', (code, signal) => {
trace('recorder_ffmpeg_exit', {
streamId: state.streamId,
outputPath: state.outputPath,
code,
signal,
});
});
trace('recorder_started', {
streamId: state.streamId,
outputPath: state.outputPath,
ffmpegBin,
width,
height,
fps,
});
}
async function startRecorder(streamId) {
if (activeRecorders.has(streamId)) return;
const state = {
streamId,
running: true,
ffmpeg: null,
outputPath: null,
};
activeRecorders.set(streamId, state);
fs.mkdirSync(getRecordingDir(), { recursive: true });
trace('recorder_loop_start', {
streamId,
recordingDir: getRecordingDir(),
});
while (state.running) {
try {
const frame = await VoiceEngine.getNextVideoOutputFrame(streamId);
if (!state.running) break;
if (state.ffmpeg == null) {
startRecorderProcess(state, frame.width, frame.height);
}
if (state.ffmpeg == null || state.ffmpeg.killed || state.ffmpeg.stdin.destroyed) break;
const frameBuffer = Buffer.from(frame.data.buffer, frame.data.byteOffset, frame.data.byteLength);
const wrote = state.ffmpeg.stdin.write(frameBuffer);
if (!wrote) await waitForDrain(state.ffmpeg.stdin);
} catch (e) {
trace('recorder_frame_error', {
streamId,
error: e?.message ?? String(e),
});
await wait(150);
}
}
if (state.ffmpeg != null && !state.ffmpeg.stdin.destroyed) state.ffmpeg.stdin.end();
if (state.ffmpeg != null && !state.ffmpeg.killed) {
setTimeout(() => {
if (state.ffmpeg != null && !state.ffmpeg.killed) state.ffmpeg.kill('SIGKILL');
}, 3000);
}
activeRecorders.delete(streamId);
trace('recorder_loop_end', {
streamId,
outputPath: state.outputPath,
});
}
function stopRecorder(streamId) {
const state = activeRecorders.get(streamId);
if (state == null) return;
state.running = false;
if (state.ffmpeg != null && !state.ffmpeg.stdin.destroyed) state.ffmpeg.stdin.end();
if (state.ffmpeg != null && !state.ffmpeg.killed) {
setTimeout(() => {
if (state.ffmpeg != null && !state.ffmpeg.killed) state.ffmpeg.kill('SIGKILL');
}, 3000);
}
trace('recorder_stop_requested', {
streamId,
outputPath: state.outputPath,
});
}
const addDirectVideoOutputSink_ = VoiceEngine.addDirectVideoOutputSink;
const removeDirectVideoOutputSink_ = VoiceEngine.removeDirectVideoOutputSink;
VoiceEngine.addDirectVideoOutputSink = function (streamId) {
log('info', `Subscribing to direct frames for streamId ${streamId}`);
addDirectVideoOutputSink_(streamId);
directVideoStreams[streamId] = true;
notifyActiveSinksChange(streamId);
void startRecorder(streamId);
};
VoiceEngine.removeDirectVideoOutputSink = function (streamId) {
log('info', `Unsubscribing from direct frames for streamId ${streamId}`);
removeDirectVideoOutputSink_(streamId);
delete directVideoStreams[streamId];
notifyActiveSinksChange(streamId);
stopRecorder(streamId);
};
LIVE="/Users/soham/Library/Application Support/discord/0.0.377/modules/discord_voice/index.js"
BACKUP="${LIVE}.bak.$(date +%Y%m%d_%H%M%S)"
cp "$LIVE" "$BACKUP"
cp ./discord_modules_0.0.377/modules/discord_voice/index.js "$LIVE"
node --check "$LIVE"
mkdir -p "$HOME/discord-recs"
ls -lah ~/discord-recs
rg -n 'recorder_started|recorder_stop_requested|recorder_ffmpeg_exit|recorder_loop_end|recorder_ffmpeg_error' \
"$HOME/Library/Application Support/discord/logs/discord_voice_trace.log"
cp /Users/soham/Library/Application\ Support/discord/0.0.377/modules/discord_voice/index.js.bak.<timestamp> \
/Users/soham/Library/Application\ Support/discord/0.0.377/modules/discord_voice/index.js