#!/bin/bash
#
# Copyright (C) 2023-2026 Eugene 'Vindex' Stulin
# Distributed under the Boost Software License 1.0.

set -eu -o pipefail

# The function prints help information.
Print_Help() {
cat <<EndOfHelp
The script saves new mediafile from source one without some piece
(from time1 to time2).

Usage:
    ${0##*/} <input/video/path> <time1> [<time2>] [-o <output_path>] [--h264]
Time format: HH:MM:SS (hours, minutes, seconds). Hours are optional.
Output file name without extension by default: "output" (extension
depends on source video).
For positioning accuracy, recording is used.
The '--h264' flag enables libx264 codec for re-encoding (by default, libx265).

Example:
    ${0##*/} video.mp4 02:10 03:35
Result is saved to output.mp4 (extension depends on source video).

Help and version:
    ${0##*/} --help|-h
    ${0##*/} --version|-v
EndOfHelp
}


# The function prints the script version.
Print_Version() {
    local VERSION_SCRIPT="$(dirname -- "${BASH_SOURCE[0]}")/bbsi-version"
    if [[ ! -x "$VERSION_SCRIPT" ]]; then
        echo "Unknown version."
    else
        "$VERSION_SCRIPT"
    fi
}


# The function prints information about wrong usage to stderr.
Wrong_Usage() {
    echo "Wrong usage. See: ${0##*/} --help" >&2
}


# parse command line arguments
if [[ $# -eq 1 ]]; then
    if [[ "$1" == "-h" || "$1" == "--help" ]]; then
        Print_Help
        exit 0
    elif [[ "$1" == "-v" || "$1" == "--version" ]]; then
        Print_Version
        exit 0
    else
        Wrong_Usage
        exit 1
    fi
fi
USE_H264=FALSE
CUSTOM_PATH=""
OPT_TEMP=$(getopt -o 'o:' --long 'h264' -n "$0" -- "$@")
if [[ $? -ne 0 ]]; then
    Wrong_Usage
    exit 1
fi
eval set -- "$OPT_TEMP"
while true; do
    case "$1" in
        '-o')
            CUSTOM_PATH="$2"
            shift 2
            continue
            ;;
        '--h264')
            USE_H264=TRUE
            shift 1
            continue
            ;;
        '--')
            shift
            break
            ;;
        *)
            echo "Internal error" >&2
            exit 1
    esac
done

if [[ $# -lt 2 ]]; then
    Wrong_Usage
    exit 1
fi

MEDIAFILE="$1"
START="$2"
FINISH=""
if [[ $# -gt 3 ]]; then
    Wrong_Usage
    exit 1
elif [[ $# -eq 3 ]]; then
    FINISH="$3"
fi


Check_Time_Format() {
    local TF='^([0-9]{1,2}:)?[0-5]?[0-9]:[0-5][0-9](\.[0-9]{1,5})?$'
    if ! [[ "$1" =~ $TF ]]; then
        Wrong_Usage
        exit 1
    fi
}
Check_Time_Format "${START}"
[[ ! -z ${FINISH} ]] && Check_Time_Format "${FINISH}"


[[ ! -z "$CUSTOM_PATH" ]] && mkdir -p "$(dirname "$CUSTOM_PATH")"
EXT="${MEDIAFILE##*.}"
[[ -z "$CUSTOM_PATH" ]] && OUTFILE="output.$EXT" || OUTFILE="$CUSTOM_PATH"
FRAGM1="tmp-1.$EXT"
FRAGM2="tmp-2.$EXT"
FRAG_LIST="tmp-list.txt"


# The function finds and prints the index of the first video stream.
# It prints the index among video streams for use with -c:v:X.
# Parameters:
#     $1 - path to video file.
# The function may print an empty result.
Get_Main_Video_Stream_Index() {
    local VIDEO_TRACKS
    if ! VIDEO_TRACKS=$(ffprobe -v error -select_streams v \
        -show_entries stream=index,codec_name -of csv=p=0 "$1"); then
        echo "Failed to analyze file." >&2
        return 1
    fi
    local INDEX=$(grep -n -v mjpeg <<< "$VIDEO_TRACKS" | cut -d: -f1 | head -1)
    if [[ -n $INDEX ]]; then
        ((INDEX--))
        echo $INDEX
    fi
}


# The function saves first part of mediafile
# using global variables MEDIAFILE and START.
# Parameters:
#     $1 - path to output file.
Extract_Begin() {
    local CODEC=libx265
    if [[ $USE_H264 == TRUE ]]; then
        CODEC=libx264
    fi
    local ENC_PARAMS="-map 0:v:0 -c:v:0 $CODEC -crf 18"
    local OTH_MAP='-map 0:a? -c:a copy -map 0:s? -c:s copy'
    ffmpeg -hide_banner -i "$MEDIAFILE" -to "$START" $ENC_PARAMS $OTH_MAP "$1"
}


# The function saves second part of mediafile
# using global variables MEDIAFILE and FINISH.
# Parameters:
#     $1 - path to output file.
Extract_Tail() {
    local ENC_PARAMS='-map 0:v:0 -c:v:0 libx265 -crf 18'
    local OTH_MAP='-map 0:a? -c:a copy -map 0:s? -c:s copy'
    ffmpeg -hide_banner -i "$MEDIAFILE" -ss "$FINISH" $ENC_PARAMS $OTH_MAP "$1"
}


Interrupt_Execution() {
    set +x
    echo "The script has been interrupted." 2>&1
    rm -f "$OUTFILE"
    exit 1
}
trap Interrupt_Execution ABRT INT QUIT TERM

Exit() {
    set +x
    rm -f "$FRAGM1" "$FRAGM2" "$FRAG_LIST"
}
trap Exit EXIT


# save media file fragment
set -x
if [[ "$START" == "00:00" || "$START" == "00:00:00" ]]; then
    if [[ -z "$FINISH" ]]; then
        cp -- "$MEDIAFILE" "$OUTFILE"
    else
        Extract_Tail "$OUTFILE"
    fi
elif [[ -z "$FINISH" ]]; then
    Extract_Begin "$OUTFILE"
else
    Extract_Begin "$FRAGM1"
    Extract_Tail "$FRAGM2"
    # concatenate two videos
    printf %b "file '$FRAGM1'\nfile '$FRAGM2'\n" >  "$FRAG_LIST"
    ffmpeg -hide_banner -f concat -safe 0 -i "$FRAG_LIST" -c copy "$OUTFILE"
    CODE=$?
    if [[ $CODE -ne 0 ]]; then
        rm -f "$OUTFILE"
        exit $CODE
    fi
fi
set +x

echo "Saved as $OUTFILE."
