diff --git a/render-chunks.sh b/render-chunks.sh index d203f04..8f3f943 100755 --- a/render-chunks.sh +++ b/render-chunks.sh @@ -1,5 +1,7 @@ #!/usr/bin/env bash -set -euo pipefail +set -o errexit -o nounset -o pipefail + +IFS=$'\n' # License MIT # Authors 2024 @@ -23,82 +25,91 @@ which ffmpeg >/dev/null || (echo "Please install ffmpeg" ; exit 1) which fzf >/dev/null || (echo "Please install fzf" ; exit 1) which mpv >/dev/null || (echo "Please install mpv" ; exit 1) -INTROS_PATH="$HOME/Dokumente/syncthing/intros" -OUTROS_PATH="$HOME/Dokumente/syncthing/outros" -CHUNKS_PATH="$HOME/Dokumente/syncthing/" -OUTPUT_PATH="$HOME/Videos/WK25/rendered" +INTROS_PATH="${HOME}/Dokumente/cloud.ctbk.de/FSCK/2025/VOC/Talk-Intros/" +OUTROS_PATH="${HOME}/Dokumente/cloud.ctbk.de/FSCK/2025/VOC/Talk-Outro/" +CHUNKS_PATH="${HOME}/Dokumente/syncthing/fsck2025/streamingrechner/Recordings/" +OUTPUT_PATH="${HOME}/Dokumente/syncthing/cwtv/cwtv-synthing/rendered/" + -# temp dir WORKDIR=$(mktemp -d) function finish { rm -r "$WORKDIR" } trap finish EXIT + +# Function to convert image to video with audio +convert_image_to_video() { + local IMAGE_FILE="${1}" + local VIDEO_FILE="${2}" + local DURATION="${3}" + local FADE_TYPE + local FADE_START + local FADE_DURATION + + if [[ "$4" == 0 ]] + then + # Fade-out + FADE_TYPE="out" + FADE_DURATION="${5}" + FADE_START="$(echo "${DURATION} - ${FADE_DURATION} - 0.02" | bc | awk '{printf "%.5f\n", $0}')" + # Note: The 0.02 = 1/50 is a hack to ensure we don't miss the last (fully black) frame + else + # Fade-in + FADE_TYPE="in" + FADE_DURATION="${4}" + FADE_START="0" + fi + + echo "Rendering fade:" + echo "Type: ${FADE_TYPE}" + echo "Start: ${FADE_START}" + echo "Duration: ${FADE_DURATION}" + + ffmpeg \ + -loop 1 \ + -framerate 50 \ + -i "${IMAGE_FILE}" \ + -f lavfi -i anullsrc=r=48000:cl=stereo \ + -vf "fade=type=${FADE_TYPE}:start_time=${FADE_START}:duration=${FADE_DURATION},format=pix_fmts=yuv420p,fps=50" \ + -c:a aac -b:a 192k \ + -c:v libx264 -threads 0 -pix_fmt yuv420p -crf 18 \ + -profile:v high -level 4.1 -disposition default -color_range tv \ + -t "${DURATION}" "${VIDEO_FILE}" +} + + +# STEP 1 # Select the appropriate files SELECTED_INTRO="$(find "$INTROS_PATH" -type f | sort --reverse --human-numeric-sort | fzf --delimiter / --with-nth -1 --prompt "Intro File:")" SELECTED_OUTRO="$(find "$OUTROS_PATH" -type f | sort --reverse --human-numeric-sort | fzf --delimiter / --with-nth -1 --prompt "Outro File:")" SELECTED_CHUNKS="$(find "$CHUNKS_PATH" -type f | sort --reverse | fzf --delimiter / --with-nth -1 -m --prompt "Video Chunks (use tab to select multiple):" | sort )" readarray -t CHUNKS_ARRAY < <(echo "$SELECTED_CHUNKS") -FOOBARWTF="$(basename "$SELECTED_INTRO")" -DEFAULT_OUTPUT_NAME="${FOOBARWTF%.*}" -read -p "Please enter a name for the outputfile (path and extension will be added automatically): " -i "$DEFAULT_OUTPUT_NAME" -e OUTPUT_NAME +BASEPATH="$(basename "${SELECTED_INTRO}")" +DEFAULT_OUTPUT_NAME="${BASEPATH%.*}" -# Function to convert image to video with audio -convert_image_to_video() { - local image_file="$1" - local output_video="$2" - local duration="$3" - local fade_in="$4" - local fade_out="$5" +read -r -p "Please enter a name for the outputfile (path and extension will be added automatically): " -i "${DEFAULT_OUTPUT_NAME}" -e OUTPUT_NAME - if [[ $fade_in == 0 ]] - then - ffmpeg -loop 1 \ - -framerate 50 \ - -t "$duration" \ - -i "$image_file" -f lavfi \ - -i anullsrc=r=48000:cl=stereo \ - -vf "fade=t=out:st=$(($duration - $fade_out)):d=$fade_out,format=pix_fmts=yuv420p,fps=50" \ - -c:a aac -b:a 192k \ - -c:v libx264 -threads 0 -pix_fmt yuv420p -crf 18 \ - -profile:v high -level 4.1 -disposition default -color_range tv \ - -metadata:s:a:0 language=native \ - -t "$duration" "$output_video" - else - ffmpeg -loop 1 \ - -framerate 50 \ - -t "$duration" \ - -i "$image_file" -f lavfi \ - -i anullsrc=r=48000:cl=stereo \ - -filter_complex "fade=t=in:st=0:d=$fade_in,format=pix_fmts=yuv420p,fps=50" \ - -c:a aac -b:a 192k \ - -c:v libx264 -threads 0 -pix_fmt yuv420p -crf 18 \ - -profile:v high -level 4.1 -disposition default -color_range tv \ - -metadata:s:a:0 language=native \ - -t "$duration" "$output_video" - fi -} +CONFIG_FILE="${OUTPUT_PATH}/${OUTPUT_NAME}.config" # find the start-offset for the first chunk -read -p "Do you want to play the first chunk ${CHUNKS_ARRAY[0]} to find the start-offset? (y/n) [n]: " PLAY_FIRST_CHUNK +read -r -p "Do you want to play the first chunk ${CHUNKS_ARRAY[0]} to find the start-offset? (y/n) [n]: " PLAY_FIRST_CHUNK PLAY_FIRST_CHUNK="${PLAY_FIRST_CHUNK:-n}" [[ "$PLAY_FIRST_CHUNK" == "y" ]] && mpv "${CHUNKS_ARRAY[0]}" --osd-level=3 --osd-status-msg='${=time-pos}' --really-quiet -read -p "Enter the start-offset in seconds for the first chunk ${CHUNKS_ARRAY[0]} [0]: " START_OFFSET +read -r -p "Enter the start-offset in seconds for the first chunk ${CHUNKS_ARRAY[0]} [0]: " START_OFFSET START_OFFSET="${START_OFFSET:-0}" - # find the end-offset for the last chunk -read -p "Do you want to play the last chunk ${CHUNKS_ARRAY[-1]} to find the end-offset? (y/n) [n]: " PLAY_LAST_CHUNK +read -r -p "Do you want to play the last chunk ${CHUNKS_ARRAY[-1]} to find the end-offset? (y/n) [n]: " PLAY_LAST_CHUNK PLAY_LAST_CHUNK="${PLAY_LAST_CHUNK:-n}" [[ "$PLAY_LAST_CHUNK" == "y" ]] && mpv "${CHUNKS_ARRAY[-1]}" --osd-level=3 --osd-status-msg='${=time-pos}' --really-quiet -read -p "Enter the end-offset in seconds for the last chunk ${CHUNKS_ARRAY[0]} [1]: " END_OFFSET +read -r -p "Enter the end-offset in seconds for the last chunk ${CHUNKS_ARRAY[0]} [1]: " END_OFFSET END_OFFSET="${END_OFFSET:-1}" # Check if intro is an image and convert if necessary @@ -117,10 +128,9 @@ if [[ "$EXT_OUTRO" == "png" || "$EXT_OUTRO" == "jpg" || "$EXT_OUTRO" == "jpeg" ] SELECTED_OUTRO="$OUTRO_VIDEO" fi -cat < "$CHUNKLIST" FFMPEG_CONCAT_CHUNKS="" if [[ ${ARRAY_LENGTH} -gt 2 ]] then - for index in $(seq 1 $(( ${ARRAY_LENGTH} - 2 )) ) + for index in $(seq 1 $(( ARRAY_LENGTH - 2 )) ) do FFMPEG_CONCAT_CHUNKS="${FFMPEG_CONCAT_CHUNKS}|${CHUNKS_ARRAY[index]}" echo "file '${CHUNKS_ARRAY[index]}'" >> "$CHUNKLIST" diff --git a/render.sh b/render.sh deleted file mode 100644 index 334175e..0000000 --- a/render.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/bash -set -euxo pipefail - -INTRO="$1" -RECORDING="$2" -OUTRO="${3:-intros/outro_ccbysa.mkv}" - -START_SECONDS="$3" -STOP_SECONDS="$4" - -DURATION_SECONDS=$(($STOP_SECONDS - $START_SECONDS)) -FADEOUT_SECONDS=$(($DURATION_SECONDS - 1)) - -ffmpeg -i "$INTRO" \ - -ss $START_SECONDS -t $DURATION_SECONDS -i "$RECORDING" \ - -i "$OUTRO" -filter_complex \ - "[1:v:0]fade=t=in:st=0:d=0.2[x];[x]fade=t=out:st=$FADEOUT_SECONDS:d=1.0[y];\ - [1:a:0]afade=t=in:st=0:d=0.2[a];[a]afade=t=out:st=$FADEOUT_SECONDS:d=1.0[b];\ - [b]dynaudnorm[bd];\ - [0:v:0][0:a:0]\ - [y][bd]\ - [2:v:0][2:a:0]\ - concat=n=3:v=1:a=1\ - [v][a0]" \ - -map '[v]' -map '[a0]' \ - -c:a aac -b:a 192k \ - -c:v libx264 -threads 0 -pix_fmt yuv420p -crf 18 -profile:v high -level 4.1 -disposition default \ - -metadata:s:a:0 language=native \ - "rendered_recordings/$(basename -s .mp4 ${INTRO})_$(basename -s .mkv ${RECORDING})_COMBINED.mkv"