194 lines
6.6 KiB
Bash
Executable File
194 lines
6.6 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Timelapse Tuner - tt
|
|
# Copyright (C) 2024 Roger Gonzalez
|
|
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
# Function to check if a command exists
|
|
command_exists() {
|
|
command -v "$1" >/dev/null 2>&1
|
|
}
|
|
|
|
# Function to print usage information
|
|
print_usage() {
|
|
echo "Usage: $0 --video <video_file1> [<video_file2> ...] --output <output_file> [--fade <fade_duration>] [--vertical]"
|
|
echo "Options:"
|
|
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)"
|
|
}
|
|
|
|
# Function to log messages with timestamps
|
|
log_message() {
|
|
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
|
|
}
|
|
|
|
# Initialize variables
|
|
video_files=()
|
|
output_file="output_video.mp4"
|
|
fade_duration=2
|
|
vertical=false
|
|
|
|
# Parse command-line arguments
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--video)
|
|
shift
|
|
while [[ $# -gt 0 && ! $1 == --* ]]; do
|
|
video_files+=("$1")
|
|
shift
|
|
done
|
|
;;
|
|
--output)
|
|
output_file="$2"
|
|
shift 2
|
|
;;
|
|
--fade)
|
|
fade_duration="$2"
|
|
shift 2
|
|
;;
|
|
--vertical)
|
|
vertical=true
|
|
shift
|
|
;;
|
|
*)
|
|
log_message "Error: Unknown option: $1"
|
|
print_usage
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
# Check if required programs exist
|
|
log_message "Checking for required programs..."
|
|
for cmd in ffmpeg ffprobe bc; do
|
|
if ! command_exists "$cmd"; then
|
|
log_message "Error: $cmd is not installed or not in PATH"
|
|
exit 1
|
|
fi
|
|
done
|
|
log_message "All required programs found"
|
|
|
|
# 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 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..."
|
|
audio_file=$(ls *.mp3 2>/dev/null | shuf -n 1)
|
|
|
|
# Check if an audio file was found
|
|
if [ -z "$audio_file" ]; then
|
|
log_message "Error: No .mp3 files found in the current directory"
|
|
exit 1
|
|
fi
|
|
log_message "Selected audio file: $audio_file"
|
|
|
|
# Get the length of the audio file in seconds
|
|
log_message "Calculating audio file length..."
|
|
audio_length=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$audio_file")
|
|
log_message "Audio length: $audio_length seconds"
|
|
|
|
log_message "Fade duration set to $fade_duration seconds"
|
|
|
|
# Prepare FFmpeg command for video scaling and concatenation
|
|
concat_filter="concat=n=${#video_files[@]}:v=1:a=0"
|
|
video_inputs=""
|
|
scaled_inputs=""
|
|
for i in "${!video_files[@]}"; do
|
|
video_inputs+="-i \"${video_files[$i]}\" "
|
|
scaled_inputs+="[${i}:v]scale=1920:1080:force_original_aspect_ratio=decrease,pad=1920:1080:(ow-iw)/2:(oh-ih)/2,setsar=1[v${i}];"
|
|
done
|
|
concat_inputs=$(for i in $(seq 0 $((${#video_files[@]}-1))); do echo -n "[v${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 ($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
|
|
log_message "Calculating fade-out start time..."
|
|
fade_out_start=$(echo "$min_length - $fade_duration" | bc)
|
|
if (( $(echo "$fade_out_start < 0" | bc -l) )); then
|
|
fade_out_start=0
|
|
fi
|
|
log_message "Fade-out starts at: $fade_out_start seconds"
|
|
|
|
# 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 - $total_video_length" | bc | cut -d'.' -f1)
|
|
if [ "$max_start" -lt 0 ]; then
|
|
random_start=0
|
|
else
|
|
random_start=$(shuf -i 0-"$max_start" -n 1)
|
|
fi
|
|
log_message "Random start time: $random_start seconds"
|
|
|
|
# Prepare FFmpeg command
|
|
ffmpeg_command="ffmpeg -y -loglevel error -stats $video_inputs -ss $random_start -i \"$audio_file\" -filter_complex \""
|
|
|
|
if $vertical; then
|
|
log_message "Converting to vertical format..."
|
|
ffmpeg_command+="$scaled_inputs $concat_inputs$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+="$scaled_inputs $concat_inputs$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\""
|
|
|
|
# Run FFmpeg command
|
|
log_message "Starting ffmpeg process..."
|
|
eval $ffmpeg_command
|
|
|
|
# Check if ffmpeg was successful
|
|
if [ $? -eq 0 ]; then
|
|
log_message "ffmpeg process completed successfully"
|
|
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"
|
|
fi
|
|
log_message "Output saved as: $output_file"
|
|
else
|
|
log_message "Error: ffmpeg process failed"
|
|
exit 1
|
|
fi
|