Refactor Timelapse Tuner to merge multiple videos with audio
This commit is contained in:
parent
0e3b2ff3c3
commit
96b3c05ac7
35
README.org
35
README.org
@ -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
69
tt
@ -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"
|
||||
|
Loading…
x
Reference in New Issue
Block a user