diff --git a/README.org b/README.org index 6127a1f..edce4f9 100644 --- a/README.org +++ b/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 --output [--fade ] [--vertical] +./tt --video [ ...] --output [--fade ] [--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: diff --git a/tt b/tt index 099a2cf..c5b252e 100755 --- a/tt +++ b/tt @@ -24,9 +24,9 @@ command_exists() { # Function to print usage information print_usage() { - echo "Usage: $0 --video --output [--fade ] [--vertical]" + echo "Usage: $0 --video [ ...] --output [--fade ] [--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"