#!/bin/bash
# Copied from Eloise Speight's Stack Exchange answer
# script to convert m4b (audiobook) files with embedded chapted (for eg. converted from Audbile) into individual chapter files

# required: ffmpeg; jg (json interpreter) & AtomicParsley (to embed pictures and add additional metadata to m4a/m4b AAC files)

# discover the file type (extension) of the input file
ext=${1##*.}
echo "extension: $ext"
# all files / folders are named based on the "shortname" of the input file
shortname=$(basename "$1" ".$ext")
picture=$shortname.jpg
chapterdata=$shortname.dat
metadata=$shortname.tmp
echo "shortname: $shortname"

# if an output type has been given on the command line, set parameters (used in ffmpeg command later)
if [[ $2 = "mp3"  ]]; then
  outputtype="mp3"
    codec="libmp3lame"
	elif [[ $2 = "m4a"  ]]; then
	  outputtype="m4a"
	    codec="copy"
		else
		  outputtype="m4b"
		    codec="copy"
			fi
			echo "outputtype: |$outputtype|"

			# if it doesn't already exist, create a json file containing the chapter breaks (you can edit this file if you want chapters to be named rather than simply "Chapter 1", etc that Audible use)
			[ ! -e "$chapterdata"  ] && ffprobe -loglevel error \
			            -i "$1" -print_format json -show_chapters -loglevel error -sexagesimal \
						            >"$chapterdata"
									read -p "Now edit the file $chapterdata if required. Press ENTER to continue."
									# comment out above if you don't want the script to pause!

									# read the chapters into arrays for later processing
									readarray -t id <<< $(jq -r '.chapters[].id' "$chapterdata")
									readarray -t start <<< $(jq -r '.chapters[].start_time' "$chapterdata")
									readarray -t end <<< $(jq -r '.chapters[].end_time' "$chapterdata")
									readarray -t title <<< $(jq -r '.chapters[].tags.title' "$chapterdata")

									# create a ffmpeg metadata file to extract addition metadata lost in splitting files - deleted afterwards
									ffmpeg -loglevel error -i "$1" -f ffmetadata "$metadata"
									artist_sort=$(sed 's/.*=\(.*\)/\1/' <<<$(cat "$metadata" |grep -m 1 ^sort_artist))
									album_sort=$(sed 's/.*=\(.*\)/\1/' <<<$(cat "$metadata" |grep -m 1 ^sort_album))
									rm "$metadata"

									# create directory for the output
									mkdir -p "$shortname"
									echo -e "\fID\tStart Time\tEnd Time\tTitle\t\tFilename"
									for i in ${!id[@]}; do
									  let trackno=$i+1
									    # set the name for output - currently in format <bookname>/<tranck number>
										  outname="$shortname/$(printf "%02d" $trackno). $shortname - ${title[$i]}.$outputtype"
										    #outname=$(sed -e 's/[^A-Za-z0-9._- ]/_/g' <<< $outname)
											  outname=$(sed 's/:/_/g' <<< $outname)
											    echo -e "${id[$i]}\t${start[$i]}\t${end[$i]}\t${title[$i]}\n\t\t$(basename "$outname")"
												  ffmpeg -loglevel error -i "$1" -vn -c $codec \
												              -ss ${start[$i]} -to ${end[$i]} \
															              -metadata title="${title[$i]}" \
																		              -metadata track=$trackno \
																					              -map_metadata 0 -id3v2_version 3 \
																								              "$outname"
																											    [[ $outputtype == m4*  ]] && AtomicParsley "$outname" \
																												            --artwork "$picture" --overWrite \
																															            --sortOrder artist "$artist_sort" \
																																		            --sortOrder album "$album_sort" \
																																					            > /dev/null
																																								done