170 lines
5.6 KiB
Bash
Executable File
170 lines
5.6 KiB
Bash
Executable File
#!/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 <input> <output.mp4>"
|
|
exit 1
|
|
fi
|
|
convert_single "$@"
|
|
;;
|
|
convert-multiple)
|
|
shift
|
|
if [[ $# -lt 3 ]]; then
|
|
error "Usage: $0 convert-multiple <input1> <input2> ... <output.mp4>"
|
|
exit 1
|
|
fi
|
|
convert_multiple "$@"
|
|
;;
|
|
*)
|
|
echo "Video Tools v$VERSION"
|
|
echo "Usage:"
|
|
echo " $0 convert-single <input> <output.mp4>"
|
|
echo " $0 convert-multiple <input1> <input2> ... <output.mp4>"
|
|
echo ""
|
|
echo "All outputs will be saved in: $OUTPUT_DIR"
|
|
;;
|
|
esac
|