Add n8n community node, /api/v1/render alias, and job webhooks for OSS automation.
Payment-free self-hosted builds keep full API access with optional webhookUrl callbacks and the published n8n-nodes-songs2vid package source under integrations/n8n.
This commit is contained in:
+47
-4
@@ -116,6 +116,38 @@ function classicScaleFilter(width: number, height: number): string {
|
||||
return `scale=${width}:${height}:force_original_aspect_ratio=decrease,pad=${width}:${height}:(ow-iw)/2:(oh-ih)/2:black`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plan-aware audio for MP4.
|
||||
* OSS / self-hosted defaults to Pro-quality 320k AAC.
|
||||
*/
|
||||
export type AudioEncodeOptions = {
|
||||
bitrateKbps: 192 | 320;
|
||||
copyWhenSafe: boolean;
|
||||
audioPath: string;
|
||||
};
|
||||
|
||||
function isCopySafeAudioPath(audioPath: string): boolean {
|
||||
const ext = path.extname(audioPath).toLowerCase();
|
||||
return ext === ".aac" || ext === ".m4a" || ext === ".mp4";
|
||||
}
|
||||
|
||||
function pushAudioEncodeArgs(args: string[], audio: AudioEncodeOptions): void {
|
||||
if (audio.copyWhenSafe && isCopySafeAudioPath(audio.audioPath)) {
|
||||
args.push("-c:a", "copy");
|
||||
return;
|
||||
}
|
||||
args.push(
|
||||
"-c:a",
|
||||
"aac",
|
||||
"-b:a",
|
||||
`${audio.bitrateKbps}k`,
|
||||
"-ar",
|
||||
"44100",
|
||||
"-ac",
|
||||
"2",
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Art-track filter that ends at `outLabel` instead of hardcoded [laid].
|
||||
*/
|
||||
@@ -149,10 +181,19 @@ export async function encodeVideo(options: {
|
||||
* When set with LOWER_LEFT / LOWER_RIGHT, blur fill uses this image; cover stays sharp.
|
||||
*/
|
||||
backgroundImagePath?: string | null;
|
||||
/** Defaults to 320k (self-hosted / studio). */
|
||||
audioBitrateKbps?: 192 | 320;
|
||||
audioCopyWhenSafe?: boolean;
|
||||
}): Promise<void> {
|
||||
const res = getResolution(options.resolution);
|
||||
if (!res) throw new Error(`Invalid resolution: ${options.resolution}`);
|
||||
|
||||
const audioEncode: AudioEncodeOptions = {
|
||||
bitrateKbps: options.audioBitrateKbps ?? 320,
|
||||
copyWhenSafe: options.audioCopyWhenSafe ?? true,
|
||||
audioPath: options.audioPath,
|
||||
};
|
||||
|
||||
await fs.mkdir(path.dirname(options.outputPath), { recursive: true });
|
||||
|
||||
const settings = normalizeWatermarkSettings(options.watermark, options.includeWatermark);
|
||||
@@ -225,8 +266,9 @@ export async function encodeVideo(options: {
|
||||
"libx264",
|
||||
"-tune",
|
||||
"stillimage",
|
||||
"-c:a",
|
||||
"copy",
|
||||
);
|
||||
pushAudioEncodeArgs(args, audioEncode);
|
||||
args.push(
|
||||
"-shortest",
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
@@ -321,8 +363,9 @@ export async function encodeVideo(options: {
|
||||
"libx264",
|
||||
"-tune",
|
||||
"stillimage",
|
||||
"-c:a",
|
||||
"copy",
|
||||
);
|
||||
pushAudioEncodeArgs(args, audioEncode);
|
||||
args.push(
|
||||
"-shortest",
|
||||
"-pix_fmt",
|
||||
"yuv420p",
|
||||
|
||||
Reference in New Issue
Block a user