#!/usr/bin/env bash italic="\033[3m" #underline="\033[4m" #ita_under="\033[3;4m" #bgd="\033[1;4;31m" red="\033[1;31m" green="\033[1;32m" yellow="\033[1;33m" bold="\033[1m" #box="\033[1;41m" reset="\033[0m" shopt -s globstar command -v jq >/dev/null 2>&1 || { echo -e "${bold}93mhandbrake_for_plex${reset} require ${bold}jq${reset} but it's not installed.\nRun ${italic}(brew install jq)${reset}\nAborting..." >&2; exit 1; } showHelp() { clear echo -e "\033[93mhandbrake_for_plex.sh\033[0m" echo "Convert and rename video files for Plex:" echo echo echo "USAGE: handbrake_for_plex.sh" echo echo "Configure:" echo " -\$SRC : source folder (recursive)" echo " -\$DEST : destination folder" echo " -\$DEST_EXT : destination extension" echo " -\$PRESET : preset HandBrake" echo " and run script..." echo echo "OPTION:" echo " -h display this help" echo " -i source folder (recursive)" echo " -o destination folder" echo " -p preset HandBrake" echo echo -e "Example: ${italic}./handbrake_for_plex.sh -i $HOME/Downloads -o $HOME/Images -p 'Apple 720p30 Surround'${reset}" echo echo -e "To known Preset, run ${italic}HandBrakeCLI --preset-list${reset}" echo exit 0 } notification() { #path_img=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) if [ "$3" -eq 0 ]; then sound="Glass" #image="$path_img/success.png" #image="$path_img/HandBrake.icns" else sound="Basso" #image="$path_img/error.png" fi if [[ "$OSTYPE" == "linux-gnu" ]] && [ -x "$(command -v zenity)" ]; then zenity --title="$1" --notification --text="$2" elif [[ "$OSTYPE" == "darwin"* ]] && [ -x "$(command -v terminal-notifier)" ]; then ### -appIcon ne marche avec BigSur # terminal-notifier -title "$1" -message "$2" -sound "$sound" -contentImage "$image" -activate "com.colliderli.iina" terminal-notifier -title "$1" -message "$2" -sound "$sound" -appIcon "https://sur-le-sentier.fr/HandBrake.png" -activate "com.colliderli.iina" # org.galad.Subler.plist com.colliderli.iina.plist -appIcon http://vjeantet.fr/images/logo.png fi } while getopts "h?i:o:p:" opt; do case "$opt" in h|\?) showHelp exit 0 ;; i) input_path="$OPTARG" ;; o) output_path="$OPTARG" ;; p) profile="$OPTARG" ;; esac done # Reset OPTIND shift $((OPTIND-1)) # https://stackoverflow.com/questions/59895/how-can-i-get-the-directory-where-a-bash-script-is-located-from-within-the-scrip #DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")") #SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) #DIR_SCRIPT=$(dirname -- "$( readlink -f -- "$0"; )") #### # Configuration # Source video folder [[ "$input_path" == "" ]] && SRC="$HOME/Downloads" || SRC="$input_path" # Destination video folder [[ "$output_path" == "" ]] && DEST="$HOME/Movies" || DEST="$output_path" # Extension video file DEST_EXT=mp4 # Handbrake preset [[ "$profile" == "" ]] && PRESET="Apple 720p30 Surround" || PRESET="$profile" # Only files larger than $MAXSIZE will be processed MAXSIZE=30000000 #MAXSIZE=300 # Open converted video file in an application APP="Subler" # log # this reduce handbrake verbosity logfile=/tmp/HandBrake.log #Move to trash after conversion trash=true ### echo "_ _ ____ _ _ ___ ___ ____ ____ _ _ ____ ____ ____ ____ ___ _ ____ _ _ "; echo "|__| |__| |\ | | \ |__] |__/ |__| |_/ |___ |___ | | |__/ |__] | |___ \/ "; echo "| | | | | \| |__/ |__] | \ | | | \_ |___ | |__| | \ | |___ |___ _/\_ "; echo " "; echo " "; echo " "; version=$(ls /opt/homebrew/Cellar/handbrake 2>/dev/null) if [ "$version" != "" ]; then HANDBRAKE_CLI="/opt/homebrew/Cellar/handbrake/$version/bin/HandBrakeCLI" echo -e "${bold}HandBrake $version${reset}" echo -e "$HANDBRAKE_CLI\n" else { cli=$(which HandBrakeCLI) if [ "$cli" != "" ]; then HANDBRAKE_CLI="$cli" version=$("$HANDBRAKE_CLI" --version 2>/dev/null | sed -n '1p') echo -e "${bold}HandBrake $version${reset}" echo -e "$HANDBRAKE_CLI\n" else echo -e "${red}HandBrake not installed!${reset}\n" exit 1 fi } fi echo -e "Source folder: ${italic}$SRC${reset}" echo -e "Destination folder: ${italic}$DEST${reset}" echo -e "HandBrake Profile: ${italic}$PRESET${reset}" # nok en bash, ok en zsh #ls -1 **/*.(mkv|mp4) #ls **/*.(mkv|avi|mp4|m4v) #ls "$HOME/Downloads"/**/*.(mkv|avi|mp4|m4v) #ls "$SRC"/**/*.{mkv,avi,mp4,m4v} count=0 # Series: SxxEyy REGEX="([sS]([0-9]{2,}|[X]{2,})[eE]([0-9]{2,}|[Y]{2,}))" # Films: 2 REGEX2="[0-9]{4}" movies=() for FILE in "${SRC}"/**/*.{mkv,avi,mp4,m4v} do # Get file size FILESIZE=$(stat -c%s "$FILE") if (( FILESIZE > MAXSIZE )); then movies+=("${FILE}") fi done nb_movies=${#movies[@]} for FILE in "${movies[@]}" do filename=$(basename "$FILE") #extension=${filename##*.} filename=${filename%.*} : <<'END_COMMENT' https://stackoverflow.com/questions/41231998/mediainfo-cli-command-line-interface-syntax-teaching-me-once-for-all media=$(mediainfo --Output=file:///$SCRIPT_DIR/template.txt "$FILE") l=$(echo "$media" | sed -n '1p') info=$(sed "1s/.*/\\${bold}$l\\${reset}/" <<< "$media") echo -e "$info\n" END_COMMENT if [[ $filename =~ $REGEX ]]; then MATCH="${BASH_REMATCH[1]}" e=$(echo "${filename%$MATCH*}" | sed 's/\./\ /g' | xargs) new_name="$e - $MATCH.$DEST_EXT" elif [[ $filename =~ $REGEX2 ]]; then MATCH2="${BASH_REMATCH[0]}" e=$(echo "${filename%$MATCH2*}" | sed 's/\./\ /g' | xargs) new_name="$e ($MATCH2).$DEST_EXT" else echo "Could not find SXXEYY pattern" new_name="$filename" continue fi if ! command -v mediainfo &> /dev/null; then echo -e "${bold}mediainfo${reset} could not be found !\n" echo -e "You should install ${bold}mediainfo${reset}:\n" echo -e " - brew install mediainfo" echo -e "" else infos=$(mediainfo "$FILE" --output=JSON | jq -s 'map({ VideoCount: .media.track[0].VideoCount, AudioCount: .media.track[0].AudioCount, TextCount: .media.track[0].TextCount, MenuCount: .media.track[0].MenuCount, FormatFichier: .media.track[0].Format, FileSize: .media.track[0].FileSize, Duration: .media.track[0].Duration, FormatVideo: .media.track[1].Format, Format_Profile: .media.track[1].Format_Profile, Format_Level: .media.track[1].Format_Level, "CodecVideo": .media.track[1].CodecID, Width: .media.track[1].Width, Height: .media.track[1].Height, FormatAudio: .media.track[2].Format, FormatAdditionalFeatures: .media.track[2].Format_AdditionalFeatures, CodecAudio: .media.track[2].CodecID, TitleAudio: .media.track[2].Title, LanguageAudio: .media.track[2].Language, DefaultAudio: .media.track[2].Default, FormatText: .media.track[3].Format, CodecText: .media.track[3].CodecID, LanguageText: .media.track[3].Language, DefaultText: .media.track[3].Default, Menu: .media.track[4].extra})') # General #nb_video=$(echo "$infos" | jq -j '.[] | .VideoCount') nb_audio=$(echo "$infos" | jq -j '.[] | .AudioCount') if [[ "$nb_audio" == "null" ]]; then nb_audio=0; fi nb_text=$(echo "$infos" | jq -j '.[] | .TextCount') if [[ "$nb_text" == "null" ]]; then nb_text=0; fi nb_menu=$(echo "$infos" | jq -j '.[] | .MenuCount') if [[ "$nb_menu" == "null" ]]; then nb_menu=0; fi format_fichier=$(echo "$infos" | jq -j '.[] | .FormatFichier') filesize=$(echo "$infos" | jq -j '.[] | .FileSize' | numfmt --to=si --format "%8.2f" | xargs) #fsize=$(numfmt --to iec --format "%8.2f" "$filesize") duree=$(echo "$infos" | jq -j '.[] | .Duration' | awk -F "." '{print $1}' | awk '{printf "%d:%02d:%02d", $1/3600, ($1/60)%60, $1%60}' | xargs) # Video format_video=$(echo "$infos" | jq -j '.[] | .FormatVideo') format_profile=$(echo "$infos" | jq -j '.[] | .Format_Profile') format_level=$(echo "$infos" | jq -j '.[] | .Format_Level') codec_video=$(echo "$infos" | jq -j '.[] | .CodecVideo') width=$(echo "$infos" | jq -j '.[] | .Width') height=$(echo "$infos" | jq -j '.[] | .Height') # Audio format_audio=$(echo "$infos" | jq -j '.[] | .FormatAudio') format_addition=$(echo "$infos" | jq -j '.[] | .FormatAdditionalFeatures') if [[ "$format_addition" == "null" ]]; then format_addition=""; fi codec_audio=$(echo "$infos" | jq -j '.[] | .CodecAudio') title_audio=$(echo "$infos" | jq -j '.[] | .TitleAudio') language_audio=$(echo "$infos" | jq -j '.[] | .LanguageAudio') if [[ "$language_audio" == "null" ]]; then language_audio=""; fi default_audio=$(echo "$infos" | jq -j '.[] | .DefaultAudio') if [[ "$default_audio" == "null" ]]; then default_audio=""; fi # Text format_text=$(echo "$infos" | jq -j '.[] | .FormatText') codec_text=$(echo "$infos" | jq -j '.[] | .CodecText') language_text=$(echo "$infos" | jq -j '.[] | .LanguageText') default_text=$(echo "$infos" | jq -j '.[] | .DefaultText') # Menu menu=$(echo "$infos" | jq -j '.[] | .Menu' | sed '1d;$d') m="" while IFS= read -r line do t=$(echo "$line" | awk -F": " '{print $1}') c=$(echo "$line" | awk -F": " '{print $2}') tt=$(echo "$t" | sed -n '1 s/"//gp' | xargs | sed 's/^_//' | sed 's/_/h/1' | sed 's/_/mm/1' | sed 's/_/s/1' | sed 's/s.*//') cc=$(echo "$c" | tr -d '"' | xargs | awk -F ":" '{print $2}' | sed 's/,$//' | sed 's/^/ /') m+="$cc : $tt\n" done <<< "$menu" echo -e "\n\n${bold}(${red}$(( count + 1 ))${reset}${bold} / $nb_movies) - $filename${reset}" echo -e "\n${bold}General:${reset}" printf " %-20s %-35s \n" "Format de fichier:" "${format_fichier}" printf " %-20s %-35s \n" "Taille de fichier:" "${filesize}" printf " %-21s %-35s \n" "Duree:" "${duree}" echo -e "\n${bold}Video:${reset}" printf " %-21s %-35s \n" "Format video:" "${format_video}" printf " %-20s %-35s \n" "Profile:" "${format_profile}" printf " %-20s %-35s \n" "Level:" "${format_level}" printf " %-20s %-35s \n" "Codec:" "${codec_video}" printf " %-20s %-35s \n" "Dimensions:" "${width} x ${height}" echo -e "\n${bold}Audio:${reset}" printf " %-20s %-35s \n" "Format audio:" "${format_audio}" printf " %-20s %-35s \n" "Format additionnel:" "${format_addition}" printf " %-20s %-35s \n" "Codec:" "${codec_audio}" printf " %-20s %-35s \n" "Titre:" "${title_audio}" printf " %-20s %-35s \n" "Language:" "${language_audio}" printf " %-20s %-35s \n" "Defaut:" "${default_audio}" if [[ "$nb_text" != 0 ]]; then echo -e "\n${bold}Text:${reset}" printf " %-20s %-35s \n" "Format text:" "${format_text}" printf " %-20s %-35s \n" "Codec:" "${codec_text}" printf " %-20s %-35s \n" "Language:" "${language_text}" printf " %-20s %-35s \n" "Defaut:" "${default_text}" fi if [[ "$nb_menu" != 0 ]]; then echo -e "\n${bold}Menu:${reset}" echo -e "$m" fi fi # if mediainfo installed file_export="$DEST"/"$new_name" if [ ! -f "$file_export" ]; then echo -e "\n${yellow}Convert $FILE${reset} ${bold}->${reset} ${green}$file_export${reset}\n" $HANDBRAKE_CLI -i "$FILE" -o "$file_export" "$PRESET" 2> $logfile result=$? if [ "$result" = 0 ]; then notification "HandBrake for Plex" "$new_name available in $DEST" $result fi # Open DEST file in an app if [ "$APP" != "" ]; then if [[ $(command -v mdfind) == "" ]]; then echo -e "\n${red}mdfind not find ! mdfind is a part macOS.${reset}" else x=$(mdfind -name "$APP" kind:application) if [ "$x" != "" ]; then if [ -f "$file_export" ]; then echo -e "\n${bold}Opening $file_export in $APP...${reset}" open -a "$APP" "$file_export" fi else echo -e "\n${red}$APP was not found...${reset}" fi fi fi # Move SRC file to trash # if [[ "$OSTYPE" == "linux-gnu" ]] && [ -x "$(command -v zenity)" ]; then # gvfs-trash filepath - gio trash some_file if [ "$trash" = true ] && [[ "$OSTYPE" == "darwin"* ]]; then echo -e "\n${italic}Move ${filename} to trash...${reset}" osascript -e "tell application \"Finder\" to delete POSIX file \"${FILE}\"" >/dev/null fi if [ "$trash" = true ] && [[ "$OSTYPE" == "linux-gnu"* ]] && [[ $(command -v gio) != "" ]]; then echo -e "\n${italic}Move ${filename} to trash...${reset}" gio trash "$FILE" >/dev/null else echo -e "\n${italic}Move ${filename} to trash...${reset}" fi echo "" ((count++)) else echo -e "\n${green}$file_export already transcoded !${reset}\n" fi FILESIZE= done if [ "$count" -eq 0 ]; then echo -e "\n${red}No file to transcode !${reset}" else echo -e "\n${green}${count} files successfully transcoded !${reset}" fi