Skip to content

Instantly share code, notes, and snippets.

@nitrag
Last active May 2, 2024 13:58
Show Gist options
  • Star 42 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save nitrag/a188b8969a539ce0f7a64deb56c00277 to your computer and use it in GitHub Desktop.
Save nitrag/a188b8969a539ce0f7a64deb56c00277 to your computer and use it in GitHub Desktop.
Audibook convert m4b to mp3. This will split into chaptered mp3 files and automatically reconfigure proper ID3v2 tags.
#!/bin/bash
#
# sudo apt-get install id3v2 ffmpeg
#
# USAGE:
# cd /book title/
# bash ~/this_script_path.sh
# rm *.m4b (you need to manually remove the original in case something goes wrong)
#
#
# EXAMPLE:
#
# $ ls
# The Wheel of Time - Book 11 - Knife of Dreams, Part 1.m4b The Wheel of Time - Book 11 - Knife of Dreams, Part 2.m4b
# $ ~/convert_m4b.sh
# .....
#
# [OUTPUT]:
#
# File: Knife of Dreams - Chapter 18 - News for the Dragon.mp3
# Metadata: ID3v2.3
# Title: Chapter 18 - News for the Dragon
# Artist: Robert Jordan
# Album: Knife of Dreams
# Track: 23
# Genre: Audiobooks
#initial track number
track=1
#assumes ( if there are multiple files Part 01/Part 02) that they are in alphabetical order
for i in *.m4b;
do
name=`echo $i | cut -d'.' -f1`;
echo $name;
ffmpeg -i "$i" -acodec libmp3lame -ar 22050 -ab 64k "$name.mp3"
full_file_path="$name.mp3"
#split chapters
title=$(pwd | sed 's,^\(.*/\)\?\([^/]*\),\2,' | cut -d , -f 1)
ffmpeg -i "$full_file_path" 2> tmp.txt
while read -r first _ _ start _ end; do
if [[ "${first}" = "Chapter" ]]
then
read # discard line with Metadata:
read _ _ chapter
chapter=$(sed -re ":r;s/\b[0-9]{1,$((1))}\b/0&/g;tr" <<<$chapter)
chapter_file="${title} - ${chapter}.mp3"
echo "processing $chapter"
</dev/null ffmpeg -loglevel error -stats -i "${full_file_path}" -ss "${start%?}" -to "${end}" -codec:a copy -metadata track="${chapter}" "${chapter_file}"
id3v2 --song "$chapter" "$chapter_file"
id3v2 --album "$title" "$chapter_file"
id3v2 --track "$track" "$chapter_file"
echo "$title - $chapter"
track=$((track+1))
fi
done <tmp.txt
rm tmp.txt
rm "$full_file_path"
done;
@lmanul
Copy link

lmanul commented May 13, 2021

Thank you so much for writing and sharing this! I was getting some usage errors with id3, so I made the following changes. I don't know if this is the best way to update the script, but it seems to work for me.

  • Line 3, replace id3 with id3v2 (apt install line)
  • Line 37, delete
  • Line 54, delete
  • Lines 55, 56, 57 replace id3 with id3v2
  • Line 55 replace --title with --song

I hope that helps!

@nitrag
Copy link
Author

nitrag commented May 13, 2021

Thanks @lmanul, I incorporated your changes

@aidyw
Copy link

aidyw commented Feb 3, 2022

Wonderful. Cheers

@alessandro-aglietti
Copy link

awesome!!!

@mangust404
Copy link

Thank you, amazing script!
But it seems due to bug in ffmpeg if m4b file contains mjpeg picture it recognizes it as video stream and output a error.
So in line 36 you can add "-vn" flag to ignore all video streams, the final command will be:
ffmpeg -i "$i" -vn -acodec libmp3lame -ar 22050 -ab 64k "$name.mp3"

and at line 50 you probably want to add $name in the resulting filename otherwise several m4b files with same chapter names, e.g. "Chapter 1, Chapter 2, ... Chapter N" will produce wrong results, so probably it should be:
chapter_file="$name - ${title} - ${chapter}.mp3"

@Thorinori
Copy link

Thanks for this! Only thing it is missing for me personally would be keeping the artist/reader if it is there, but that is a minor tweak I can do manually when needed :)

@yellomello
Copy link

Works flawlessly!

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