Skip to content

Instantly share code, notes, and snippets.

@aularon
Last active June 7, 2024 14:44
Show Gist options
  • Save aularon/c48173f8246fa57e9c1ef7ff694ab06f to your computer and use it in GitHub Desktop.
Save aularon/c48173f8246fa57e9c1ef7ff694ab06f to your computer and use it in GitHub Desktop.
Split an m4b into its chapters. No recoding is done, just splitting
#!/bin/bash
# Description: Split an m4b into its chapters. No recoding is done, just splitting
# Usage: m4b_split.sh $input_file $output_dir/
# Requires: ffmpeg, jq
# Author: Hasan Arous
# License: MIT
in="$1"
out="$2"
splits=""
while read start end title; do
splits="$splits -c copy -ss $start -to $end $out/$title.m4b"
done <<<$(ffprobe -i "$in" -print_format json -show_chapters \
| jq -r '.chapters[] | .start_time + " " + .end_time + " " + (.tags.title | sub(" "; "_"))')
ffmpeg -i "$in" $splits
@michaellammers
Copy link

Great, thanks! 🙏

@michaellammers This version handles spaces and chapters that are not alphabetically sorted, but it requires zsh to run. I did that because zsh handles spaces in variable names and arrays, while bash is tricky. Note that this does not and cannot speed up the audio, because it makes a lossless copy (as in the original version of the gist):

#!/bin/zsh
if [[ -z $1 || -z $2 ]]; then
    echo "Usage: $0 <in file> <out directory>" >&2
    exit 1
fi

in=$1
out=$2
mkdir -p $out

chapters_str=$(ffprobe -i $in -print_format json -show_chapters | \
    jq -r '.chapters[] | .start_time + " " + .end_time + " " + (.tags.title | sub(" "; "_"))')
# Prefix width, like 3 for "001", "002"...
width=$(($(echo $chapters_str | wc -l | wc -c) - 1))
n=0
splits=()
echo $chapters_str | \
while read start end title; do
    ((n++))
    splits+=(-c copy -c:a copy -map 0:a -ss $start -to $end "$out/${(l:$width::0:)n} - $title.m4b")
done

ffmpeg -i $in $splits

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment