diff --git a/README.md b/README.md index 8740947..36126e2 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,7 @@ Edit specs are JavaScript / JSON objects describing the whole edit operation wit }, }, audioFilePath, + loopAudio: false, keepSourceAudio: false, clips: [ { @@ -155,6 +156,7 @@ Edit specs are JavaScript / JSON objects describing the whole edit operation wit | `height` | `--height` | Height which all media will be converted to | auto based on `width` and aspect ratio of **first video** | | | `fps` | `--fps` | FPS which all videos will be converted to | First video FPS or `25` | | | `audioFilePath` | `--audio-file-path` | Set an audio track for the whole video | | | +| `loopAudio` | `--loop-audio` | Loop the audio track if it is shorter than video? | | | | `keepSourceAudio` | `--keep-source-audio` | Keep audio from source files | | | | `fast` | `--fast`, `-f` | Fast mode (low resolution and FPS, useful for getting a quick preview) | `false` | | | `defaults.layer.fontPath` | `--font-path` | Set default font to a .ttf | System font | | diff --git a/cli.js b/cli.js index 83ebd1b..88a02af 100644 --- a/cli.js +++ b/cli.js @@ -32,6 +32,7 @@ const cli = meow(` --fps FPS which all videos will be converted to --font-path Set default font to a .ttf --audio-file-path Add an audio track + --loop-audio Loop the audio track if it is shorter than video? --keep-source-audio Keep audio from source files --fast, -f Fast mode (low resolution and FPS, useful for getting a quick preview) @@ -53,6 +54,7 @@ const cli = meow(` width: { type: 'number' }, height: { type: 'number' }, fps: { type: 'number' }, + loopAudio: { type: 'boolean' }, }, }); @@ -94,7 +96,7 @@ const cli = meow(` params.clips = clips.map((clip) => ({ layers: [clip] })); } - const { verbose, transitionName, transitionDuration, clipDuration, width, height, fps, audioFilePath, fontPath, fast, out: outPath, keepSourceAudio } = cli.flags; + const { verbose, transitionName, transitionDuration, clipDuration, width, height, fps, audioFilePath, fontPath, fast, out: outPath, keepSourceAudio, loopAudio } = cli.flags; if (transitionName || transitionDuration != null) { params.defaults.transition = {}; @@ -112,6 +114,7 @@ const cli = meow(` if (outPath) params.outPath = outPath; if (audioFilePath) params.audioFilePath = audioFilePath; + if (loopAudio) params.loopAudio = loopAudio; if (keepSourceAudio) params.keepSourceAudio = true; if (width) params.width = width; if (height) params.height = height; diff --git a/examples/audioLoop.json5 b/examples/audioLoop.json5 new file mode 100644 index 0000000..56c0505 --- /dev/null +++ b/examples/audioLoop.json5 @@ -0,0 +1,10 @@ +{ + outPath: './audioLoop.mp4', + audioFilePath: './assets/winxp.mp3', + loopAudio: true, + // Should properly cut off and not crash with EPIPE if loopAudio=false and audio duration is shorter than total duration + // loopAudio: false, + clips: [ + { duration: 10, layers: [{ type: 'title-background', text: 'Looping audio!' }] }, + ], +} diff --git a/index.js b/index.js index 69e6e5b..f190fba 100644 --- a/index.js +++ b/index.js @@ -37,6 +37,7 @@ module.exports = async (config = {}) => { fps: requestedFps, defaults: defaultsIn = {}, audioFilePath: audioFilePathIn, + loopAudio, keepSourceAudio, ffmpegPath = 'ffmpeg', @@ -365,6 +366,8 @@ module.exports = async (config = {}) => { '-y', outPath, ]; + const loopAudioArgs = loopAudio ? ['-stream_loop', '-1'] : []; + const args = [ ...(enableFfmpegLog ? [] : ['-hide_banner', '-loglevel', 'error']), @@ -375,7 +378,7 @@ module.exports = async (config = {}) => { '-r', framerateStr, '-i', '-', - ...(audioFilePath ? ['-i', audioFilePath, '-shortest'] : []), + ...(audioFilePath ? [...loopAudioArgs, '-i', audioFilePath, '-shortest'] : []), '-map', '0:v:0', ...(audioFilePath ? ['-map', '1:a:0'] : []),