Perl Script to Convert an Audiobook (m4b) to mp3 files splitting on the chapters

I have an Nissan Altima with a BOSE radio that allows me to hook up an USB thumbdrive containing mp3 files. The problem is most of my audiobooks are in m4b format. Previously I’ve used tools like mp3splt and tried to split on ‘silence’ or timed increments (say 15 min) but I was getting mp3 files that would be split in midsentence and sometimes midword. It became very annoying after awhile.

So, I came up with a really simple script to convert an audiobook (m4b) into mp3 files splitting on the chapters. We are dependent on FFmpeg::Command and a modified FFprobe Perl module.

In the following example, we are converting a Ben Bova audiobook but we are going to specify to start the track numbering at “10” because the 2nd file ended with track “9”.

jason@jason-Inspiron-1545 ~/bin $ ./test_mp4_info.pl -i "/home/jason/Audiobooks/Ben Bova/Mars/Mars 3.m4b" -o mp3 -a "Mars" -t 10
Converting "Mars 3.m4b" to "mp3/010 Mars.mp3"...
	album: Mars
	artist: Ben Bova
	title: 010 - Mars
	genre: Audiobook
	track: 10
	... COMPLETE
Converting "Mars 3.m4b" to "mp3/011 Mars.mp3"...
	album: Mars
	artist: Ben Bova
	title: 011 - Mars
	genre: Audiobook
	track: 11
	... COMPLETE
Converting "Mars 3.m4b" to "mp3/012 Mars.mp3"...
	album: Mars
	artist: Ben Bova
	title: 012 - Mars
	genre: Audiobook
	track: 12
	... COMPLETE
Converting "Mars 3.m4b" to "mp3/013 Mars.mp3"...
	album: Mars
	artist: Ben Bova
	title: 013 - Mars
	genre: Audiobook
	track: 13
	... COMPLETE
Converting "Mars 3.m4b" to "mp3/014 Mars.mp3"...
	album: Mars
	artist: Ben Bova
	title: 014 - Mars
	genre: Audiobook
	track: 14
	... COMPLETE

Source code:

#!/usr/bin/perl

use strict;
use warnings;

use lib qw(/home/jason/bin);

use Getopt::Std;
use File::Basename;
use FFmpeg::Command;
use FFprobe;

$|++;

###############################
sub _encode_mp3 {
  my ($input_file, $output_dir, $album, $starting_track) = @_;

  my %tags = ();
  my $track_number;
  my $mp4 = FFprobe->probe_file($input_file);
  my $base_output_file = basename($input_file);
  $base_output_file =~ s/\.\w+$//;

  if (exists $mp4->{format}->{'TAG:comment'}) {
    $tags{genre} = $mp4->{format}->{'TAG:comment'};
    $tags{genre} =~ s/("')//g;
  }

  if (exists $mp4->{format}->{'TAG:genre'}) {
    $tags{genre} = $mp4->{format}->{'TAG:genre'};
    $tags{genre} =~ s/("')//g;
  }

  if (exists $mp4->{format}->{'TAG:artist'}) {
    $tags{artist} = $mp4->{format}->{'TAG:artist'};
    $tags{artist} =~ s/("')//g;
  }

  if ($album) {
    $tags{album} = $album;
  } elsif (exists $mp4->{format}->{'TAG:album'}) {
    $tags{album} = $mp4->{format}->{'TAG:album'};
  }

  $tags{album} =~ s/("')//g;
  $track_number = $starting_track if $starting_track;

  foreach my $chapter (sort keys %{$mp4->{chapters}}) {
    unless ($starting_track) {
      $track_number = $chapter;
    }

    my $output_file = sprintf "%s/%03d %s.mp3", $output_dir, $track_number, $tags{album};
    my $start = $mp4->{chapters}->{$chapter}->{start};
    my $duration = $mp4->{chapters}->{$chapter}->{end} - $start;
    my @options = ();

    if ($album) {
      $tags{title} = sprintf "%03d - %s", $track_number, $album;
    } else {
      if (exists $mp4->{format}->{'TAG:title'}) {
        $tags{title} = sprintf "%03d - %s", $track_number, $mp4->{format}->{'TAG:title'};
      } else {
        $tags{title} = sprintf "%03d - %s", $track_number, $base_output_file;
      }
    }

    $tags{title} =~ s/("')//g;

    my $ffmpeg = FFmpeg::Command->new;

    $ffmpeg->input_options({
        file => $input_file,
     });

    $ffmpeg->output_options({
     'file' => $output_file,
     'audio_codec' => 'libmp3lame',
     'audio_bit_rate' => 64,
     });

    printf "Converting \"%s\" to \"%s\"...\n", basename($input_file), $output_file;

Share Button

3 thoughts on “Perl Script to Convert an Audiobook (m4b) to mp3 files splitting on the chapters

  1. Sugel

    I have other, general comments, but one about the MP3s in particular that’s proven very difficult for me as a Primary teacher: differentiating between the “with words” and “without words” versions. I have a filter in place on my download program, which keeps me from downloading “with words” files into my “without words” folder (I think it went something like /2/1/301.mp3 = hymnal, with words, page 301, while 2/2/301.mp3 = hymnal, without words, page 301.) Having “w” (or something) appended to the track number would be… nifty.

Comments are closed.