Refactor Timelapse Tuner to merge multiple videos with audio

This commit is contained in:
Roger Gonzalez 2024-10-19 13:41:20 -03:00
parent 0e3b2ff3c3
commit 96b3c05ac7
Signed by: rogs
GPG Key ID: C7ECE9C6C36EC2E6
2 changed files with 62 additions and 42 deletions

View File

@ -2,7 +2,7 @@
[[https://gitlab.com/uploads/-/system/project/avatar/62789004/timelapse_tuner.png]]
Timelapse Tuner is a Bash script that automates the process of creating timelapse videos with randomly selected background music. It merges a timelapse video file with a randomly chosen audio file from the current directory, applying fade-in and fade-out effects to the audio for a polished result. It now supports both horizontal and vertical video output!
Timelapse Tuner is a Bash script that automates the process of creating timelapse videos with randomly selected background music. It can merge multiple timelapse video files and combine them with a randomly chosen audio file from the current directory, applying fade-in and fade-out effects to the audio for a polished result. It supports both horizontal and vertical video output!
* Video Demo
:PROPERTIES:
@ -18,10 +18,11 @@ Timelapse Tuner is a Bash script that automates the process of creating timelaps
:ID: f0a43fa6-6a89-409e-a72b-83145dd70e0c
:END:
- Merges a specified timelapse video file with a randomly selected MP3 audio file
- Merges multiple timelapse video files into a single video
- Combines the merged video with a randomly selected MP3 audio file
- Applies fade-in and fade-out effects to the audio for smooth transitions
- Randomly selects a start point in the audio file to add variety
- Supports custom input timelapse video and output file specification
- Supports custom input timelapse videos and output file specification
- Allows customization of fade duration
- Provides option to convert horizontal videos to vertical format
- Provides detailed logging of the merging process
@ -66,7 +67,7 @@ You will also need to provide your own MP3 files for background music. If you ne
Run the script with the following command:
#+BEGIN_SRC sh
./tt --video <input_timelapse_video> --output <output_file> [--fade <fade_duration>] [--vertical]
./tt --video <video_file1> [<video_file2> ...] --output <output_file> [--fade <fade_duration>] [--vertical]
#+END_SRC
** Options:
@ -74,7 +75,7 @@ Run the script with the following command:
:ID: 9afd9a28-f9bf-47f0-a154-ff51f72c67e8
:END:
- =--video=: Specify the input timelapse video file (required)
- =--video=: Specify one or more input video files (at least one is required)
- =--output=: Specify the output MP4 file name (optional, default: output_video.mp4)
- =--fade=: Specify the fade duration in seconds (optional, default: 2)
- =--vertical=: Convert the video to vertical format (optional, default: horizontal)
@ -84,14 +85,14 @@ Run the script with the following command:
:ID: 0b930521-693e-49de-af95-fc7004264b30
:END:
For horizontal output (default):
For merging multiple horizontal videos (default):
#+BEGIN_SRC sh
./tt --video my_timelapse.mp4 --output final_timelapse.mp4 --fade 3
./tt --video timelapse1.mp4 timelapse2.mp4 timelapse3.mp4 --output merged_timelapse.mp4 --fade 3
#+END_SRC
For vertical output:
For merging multiple videos and converting to vertical format:
#+BEGIN_SRC sh
./tt --video my_timelapse.mp4 --output vertical_timelapse.mp4 --fade 3 --vertical
./tt --video timelapse1.mp4 timelapse2.mp4 --output vertical_timelapse.mp4 --fade 3 --vertical
#+END_SRC
* How It Works
@ -100,14 +101,15 @@ For vertical output:
:END:
1. The script checks for the required dependencies (FFmpeg, FFprobe, bc).
2. It verifies the input timelapse video file exists.
2. It verifies that all input video files exist.
3. A random MP3 file is selected from the current directory to serve as background music.
4. The lengths of both the timelapse video and audio files are calculated.
5. Fade-in and fade-out durations are set (default 2 seconds, customizable with --fade) for smooth audio transitions.
6. A random start point in the audio file is selected to ensure variety in the background music.
7. If the --vertical option is used, the video is scaled and cropped to 1080x1920 resolution.
8. FFmpeg is used to merge the timelapse video with the selected portion of the audio, applying fade effects and any necessary format conversion.
9. The final timelapse video with background music is saved as an MP4 file.
4. The lengths of all video files and the audio file are calculated.
5. If multiple video files are provided, they are concatenated in the order specified.
6. Fade-in and fade-out durations are set (default 2 seconds, customizable with --fade) for smooth audio transitions.
7. A random start point in the audio file is selected to ensure variety in the background music.
8. If the --vertical option is used, the combined video is scaled and cropped to 1080x1920 resolution.
9. FFmpeg is used to merge the concatenated video with the selected portion of the audio, applying fade effects and any necessary format conversion.
10. The final timelapse video with background music is saved as an MP4 file.
* Providing Background Music
:PROPERTIES:
@ -136,6 +138,7 @@ If you encounter any issues:
2. Check that you have read/write permissions in the current directory.
3. Verify that there are MP3 files in the same directory as the script for background music.
4. If FFmpeg fails, check the error message for details on what went wrong.
5. When using multiple input videos, ensure they have compatible formats and resolutions.
* Contributing
:PROPERTIES:

69
tt
View File

@ -24,9 +24,9 @@ command_exists() {
# Function to print usage information
print_usage() {
echo "Usage: $0 --video <video_file> --output <output_file> [--fade <fade_duration>] [--vertical]"
echo "Usage: $0 --video <video_file1> [<video_file2> ...] --output <output_file> [--fade <fade_duration>] [--vertical]"
echo "Options:"
echo " --video Specify the input video file"
echo " --video Specify one or more input video files"
echo " --output Specify the output MP4 file name (default: output_video.mp4)"
echo " --fade Specify the fade duration in seconds (default: 2)"
echo " --vertical Convert the video to vertical format (default: horizontal)"
@ -38,7 +38,7 @@ log_message() {
}
# Initialize variables
video_file=""
video_files=()
output_file="output_video.mp4"
fade_duration=2
vertical=false
@ -47,8 +47,11 @@ vertical=false
while [[ $# -gt 0 ]]; do
case $1 in
--video)
video_file="$2"
shift 2
shift
while [[ $# -gt 0 && ! $1 == --* ]]; do
video_files+=("$1")
shift
done
;;
--output)
output_file="$2"
@ -80,19 +83,21 @@ for cmd in ffmpeg ffprobe bc; do
done
log_message "All required programs found"
# Check if a video file is provided
if [ -z "$video_file" ]; then
log_message "Error: No video file specified"
# Check if video files are provided
if [ ${#video_files[@]} -eq 0 ]; then
log_message "Error: No video files specified"
print_usage
exit 1
fi
# Check if the video file exists
if [ ! -f "$video_file" ]; then
log_message "Error: Video file '$video_file' not found"
exit 1
fi
log_message "Input video file: $video_file"
# Check if all video files exist
for video_file in "${video_files[@]}"; do
if [ ! -f "$video_file" ]; then
log_message "Error: Video file '$video_file' not found"
exit 1
fi
done
log_message "Input video files: ${video_files[*]}"
# Pick a random .mp3 file from the current directory
log_message "Selecting a random MP3 file..."
@ -112,14 +117,24 @@ log_message "Audio length: $audio_length seconds"
log_message "Fade duration set to $fade_duration seconds"
# Get the length of the video file in seconds
log_message "Calculating video file length..."
video_length=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$video_file")
log_message "Video length: $video_length seconds"
# Prepare FFmpeg command for video concatenation
concat_filter="concat=n=${#video_files[@]}:v=1:a=0"
video_inputs=""
for i in "${!video_files[@]}"; do
video_inputs+="-i \"${video_files[$i]}\" "
done
# Calculate total video length
total_video_length=0
for video_file in "${video_files[@]}"; do
video_length=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$video_file")
total_video_length=$(echo "$total_video_length + $video_length" | bc)
done
log_message "Total video length: $total_video_length seconds"
# Use the shorter of the two lengths for fade-out calculation
log_message "Calculating minimum length for fade-out..."
min_length=$(echo "if ($video_length < $audio_length) $video_length else $audio_length" | bc)
min_length=$(echo "if ($total_video_length < $audio_length) $total_video_length else $audio_length" | bc)
log_message "Minimum length: $min_length seconds"
# Ensure fade-out starts at a valid time
@ -130,9 +145,9 @@ if (( $(echo "$fade_out_start < 0" | bc -l) )); then
fi
log_message "Fade-out starts at: $fade_out_start seconds"
# Calculate a random start time within the audio length minus the video length
# Calculate a random start time within the audio length minus the total video length
log_message "Calculating random start time for audio..."
max_start=$(echo "$audio_length - $video_length" | bc | cut -d'.' -f1)
max_start=$(echo "$audio_length - $total_video_length" | bc | cut -d'.' -f1)
if [ "$max_start" -lt 0 ]; then
random_start=0
else
@ -141,15 +156,17 @@ fi
log_message "Random start time: $random_start seconds"
# Prepare FFmpeg command
ffmpeg_command="ffmpeg -loglevel error -stats -i \"$video_file\" -ss $random_start -i \"$audio_file\""
ffmpeg_command="ffmpeg -loglevel error -stats $video_inputs -ss $random_start -i \"$audio_file\" -filter_complex \""
if $vertical; then
log_message "Converting to vertical format..."
ffmpeg_command+=" -filter_complex \"[0:v]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,setsar=1[v];[1:a]afade=t=in:st=0:d=$fade_duration,afade=t=out:st=$fade_out_start:d=$fade_duration[a]\""
ffmpeg_command+="$concat_filter [v_concat]; [v_concat]scale=1080:1920:force_original_aspect_ratio=increase,crop=1080:1920,setsar=1[v];"
ffmpeg_command+="[${#video_files[@]}:a]afade=t=in:st=0:d=$fade_duration,afade=t=out:st=$fade_out_start:d=$fade_duration[a]\""
ffmpeg_command+=" -map \"[v]\" -map \"[a]\""
else
ffmpeg_command+=" -filter_complex \"[1:a]afade=t=in:st=0:d=$fade_duration,afade=t=out:st=$fade_out_start:d=$fade_duration[a]\""
ffmpeg_command+=" -map 0:v -map \"[a]\""
ffmpeg_command+="$concat_filter [v_concat];"
ffmpeg_command+="[${#video_files[@]}:a]afade=t=in:st=0:d=$fade_duration,afade=t=out:st=$fade_out_start:d=$fade_duration[a]\""
ffmpeg_command+=" -map \"[v_concat]\" -map \"[a]\""
fi
ffmpeg_command+=" -c:v libx264 -c:a aac -shortest \"$output_file\""
@ -161,7 +178,7 @@ eval $ffmpeg_command
# Check if ffmpeg was successful
if [ $? -eq 0 ]; then
log_message "ffmpeg process completed successfully"
log_message "Merged $video_file with random audio: $audio_file"
log_message "Merged ${#video_files[@]} video files with random audio: $audio_file"
log_message "Audio starts at $random_start seconds, with fade-in/out effects"
if $vertical; then
log_message "Video converted to vertical format"