#!/usr/bin/env bash # # Simple FFmpeg CLI Toolset # ------------------------- # Provides easy commands for single or multi-file video conversion. # Works on Linux and Windows (Git Bash / WSL) as long as ffmpeg is installed. # set -euo pipefail # ========================================================== # Configuration system # ========================================================== CONFIG_FILE="$(dirname "$0")/config/config.json" # ------------------------- # Default configuration # ------------------------- OUTPUT_DIR="$HOME/Videos" VIDEO_CODEC="libx264" AUDIO_CODEC="aac" CRF="18" # lower = higher quality PRESET="slow" # slower = better compression AUDIO_BITRATE="192k" VERSION="0.1.0" # ========================================================== # Utility: Colour logging # ========================================================== RED="\033[31m" YELLOW="\033[33m" GREEN="\033[32m" BLUE="\033[34m" NC="\033[0m" # reset info() { echo -e "${BLUE}[INFO]${NC} $*"; } success() { echo -e "${GREEN}[OK]${NC} $*"; } warn() { echo -e "${YELLOW}[WARN]${NC} $*"; } error() { echo -e "${RED}[ERROR]${NC} $*" >&2; } # ========================================================== # Check dependencies early # ========================================================== if ! command -v ffmpeg >/dev/null 2>&1; then error "ffmpeg is not installed or not in PATH." exit 1 fi if [[ -f "$CONFIG_FILE" ]] && command -v jq >/dev/null 2>&1; then info "Loading configuration from: $CONFIG_FILE" OUTPUT_DIR="$(eval echo "$(jq -r '.output_dir // empty' "$CONFIG_FILE")")" || true VIDEO_CODEC="$(jq -r '.video_codec // empty' "$CONFIG_FILE" 2>/dev/null || echo "$VIDEO_CODEC")" AUDIO_CODEC="$(jq -r '.audio_codec // empty' "$CONFIG_FILE" 2>/dev/null || echo "$AUDIO_CODEC")" CRF="$(jq -r '.crf // empty' "$CONFIG_FILE" 2>/dev/null || echo "$CRF")" PRESET="$(jq -r '.preset // empty' "$CONFIG_FILE" 2>/dev/null || echo "$PRESET")" AUDIO_BITRATE="$(jq -r '.audio_bitrate // empty' "$CONFIG_FILE" 2>/dev/null || echo "$AUDIO_BITRATE")" VERSION="$(jq -r '.version // empty' "$CONFIG_FILE" 2>/dev/null || echo "$VERSION")" elif [[ ! -f "$CONFIG_FILE" ]]; then warn "No config.json found — using internal defaults." else warn "jq not found — skipping config.json parsing." fi mkdir -p "$OUTPUT_DIR" || { error "Failed to create output directory $OUTPUT_DIR"; exit 1; } # ========================================================== # Header output # ========================================================== print_header() { echo "----------------------------------------------" echo "Video Tools v$VERSION" echo "Output directory: $OUTPUT_DIR" echo "Video codec : $VIDEO_CODEC" echo "Audio codec : $AUDIO_CODEC" echo "CRF : $CRF" echo "Preset : $PRESET" echo "Audio rate : $AUDIO_BITRATE" echo "----------------------------------------------" } # ========================================================== # Convert one file # ========================================================== convert_single() { local input="$1" local output_name="$2" local output_path="$OUTPUT_DIR/$output_name" print_header info "Converting single video..." info "Input : $input" info "Output: $output_path" if [[ ! -f "$input" ]]; then error "Input file not found: $input" exit 1 fi ffmpeg -hide_banner -loglevel info -fflags +genpts \ -i "$input" \ -c:v "$VIDEO_CODEC" -crf "$CRF" -preset "$PRESET" \ -c:a "$AUDIO_CODEC" -b:a "$AUDIO_BITRATE" -movflags +faststart \ "$output_path" || { error "Conversion failed."; exit 1; } success "Conversion complete: $output_path" } # ========================================================== # Combine multiple files # ========================================================== convert_multiple() { local args=("$@") local output_name="${args[-1]}" unset 'args[-1]' local list_file list_file=$(mktemp) local output_path="$OUTPUT_DIR/$output_name" print_header info "Combining multiple videos..." for input in "${args[@]}"; do if [[ ! -f "$input" ]]; then error "Missing input file: $input" rm -f "$list_file" exit 1 fi echo "file '$input'" >> "$list_file" info " + Added: $input" done echo "----------------------------------------------" ffmpeg -hide_banner -loglevel info -f concat -safe 0 -i "$list_file" \ -c:v "$VIDEO_CODEC" -crf "$CRF" -preset "$PRESET" \ -c:a "$AUDIO_CODEC" -b:a "$AUDIO_BITRATE" -movflags +faststart \ "$output_path" || { error "Merge failed."; rm -f "$list_file"; exit 1; } rm -f "$list_file" success "Combined video created: $output_path" } # ========================================================== # Dispatcher # ========================================================== case "${1:-}" in convert-single) shift if [[ $# -ne 2 ]]; then error "Usage: $0 convert-single " exit 1 fi convert_single "$@" ;; convert-multiple) shift if [[ $# -lt 3 ]]; then error "Usage: $0 convert-multiple ... " exit 1 fi convert_multiple "$@" ;; *) echo "Video Tools v$VERSION" echo "Usage:" echo " $0 convert-single " echo " $0 convert-multiple ... " echo "" echo "All outputs will be saved in: $OUTPUT_DIR" ;; esac