#!/usr/bin/env bash set -euo pipefail # remove_text.sh — conceal on-screen text in a rectangular area for a chosen time window. # Requires: ffmpeg (with delogo filter) show_help() { cat <<'EOF' Usage: remove_text.sh -i INPUT -o OUTPUT --x X --y Y --w W --h H [--start SEC] [--end SEC] [--feather PX] [--preview] [--dry-run] Params: -i, --input Input video file -o, --output Output video file --x Left coordinate of the box (pixels) --y Top coordinate of the box (pixels) --w Box width (pixels) --h Box height (pixels) --start Start time in seconds (e.g. 12 or 12.5). If omitted, applies from t=0. --end End time in seconds (exclusive). If omitted, applies to end of video. --feather Feather thickness for delogo (default: 8). Higher = softer edge. --preview Instead of removing, draw a red box to help align the area. --dry-run Print the ffmpeg command but don't execute it. -h, --help Show this help Examples: Preview the area (draws box only): remove_text.sh -i in.mp4 -o preview.mp4 --x 120 --y 940 --w 680 --h 60 --start 12.5 --end 28.3 --preview Remove text in that area/time range: remove_text.sh -i in.mp4 -o out.mp4 --x 120 --y 940 --w 680 --h 60 --start 12.5 --end 28.3 EOF } # ---- defaults ---- INPUT="" OUTPUT="" X="" Y="" W="" H="" START="" END="" FEATHER="8" PREVIEW="0" DRYRUN="0" # ---- parse args ---- if [[ $# -eq 0 ]]; then show_help; exit 1; fi while [[ $# -gt 0 ]]; do case "$1" in -i|--input) INPUT="${2:-}"; shift 2;; -o|--output) OUTPUT="${2:-}"; shift 2;; --x) X="${2:-}"; shift 2;; --y) Y="${2:-}"; shift 2;; --w) W="${2:-}"; shift 2;; --h) H="${2:-}"; shift 2;; --start) START="${2:-}"; shift 2;; --end) END="${2:-}"; shift 2;; --feather) FEATHER="${2:-}"; shift 2;; --preview) PREVIEW="1"; shift;; --dry-run) DRYRUN="1"; shift;; -h|--help) show_help; exit 0;; *) echo "Unknown argument: $1"; echo; show_help; exit 1;; esac done # ---- validation ---- if [[ -z "$INPUT" || -z "$OUTPUT" || -z "$X" || -z "$Y" || -z "$W" || -z "$H" ]]; then echo "Error: Missing required arguments."; echo show_help; exit 1 fi if ! command -v ffmpeg >/dev/null 2>&1; then echo "Error: ffmpeg not found in PATH."; exit 1 fi # Build enable expression based on start/end ENABLE_EXPR="" if [[ -n "$START" && -n "$END" ]]; then ENABLE_EXPR="between(t\,${START}\,${END})" elif [[ -n "$START" ]]; then ENABLE_EXPR="gte(t\,${START})" elif [[ -n "$END" ]]; then ENABLE_EXPR="lte(t\,${END})" else ENABLE_EXPR="" # apply for whole video fi # Build filter chain if [[ "$PREVIEW" == "1" ]]; then if [[ -n "$ENABLE_EXPR" ]]; then VFILTER="drawbox=x=${X}:y=${Y}:w=${W}:h=${H}:color=red@0.5:thickness=2:enable='${ENABLE_EXPR}'" else VFILTER="drawbox=x=${X}:y=${Y}:w=${W}:h=${H}:color=red@0.5:thickness=2" fi else # delogo: x, y, w, h, t = feather thickness; show=0 (no outline) # delogo: x, y, w, h, band = feather thickness; show=0 (no outline) if [[ -n "$ENABLE_EXPR" ]]; then VFILTER="delogo=x=${X}:y=${Y}:w=${W}:h=${H}:show=0:enable='${ENABLE_EXPR}'" else VFILTER="delogo=x=${X}:y=${Y}:w=${W}:h=${H}:show=0" fi fi # Assemble ffmpeg command # -c:v libx264 -crf 18: quality/size balance; adjust as needed # -preset medium: speed/efficiency tradeoff # -c:a copy: keep original audio CMD=(ffmpeg -hide_banner -y -i "$INPUT" -vf "$VFILTER" -c:v libx264 -crf 18 -preset medium -c:a copy "$OUTPUT") echo "Running:" printf ' %q' "${CMD[@]}"; echo if [[ "$DRYRUN" == "1" ]]; then exit 0 fi "${CMD[@]}" echo "Done: $OUTPUT"