2

I'm trying to convert some audiobooks using a script I found online, but I think there might be something wrong with the script, since it does not seem to work as intended.

The audiobooks from Audible are in .aax format.


The script:

#!/bin/bash
# audiblefreedom
# audiblefreedom is a simple Linux program that strips the DRM from Audible
# audiobooks, splits it into chapters, tags and names the files and converts
# them to mp3 format.
#
# REQUIREMENTS
# Requires mkvmerge, ffmpeg, lame
# if using Ubuntu 14.04, a ppa is required for ffmpeg
# sudo add-apt-repository ppa:mc3man/trusty-media
# sudo apt-get update
# sudo apt-get install mkvtoolnix ffmpeg lame
# 
# You must also use audible-activator
# (https://github.com/inAudible-NG/audible-activator/) to get your activation
# bytes (to decrypt the AAX files) and save them as a file called bytes.txt in
# the same dir as the script. Note, this will only decrypt AAX files purchased
# on this account.
#
# USAGE
# audiblefreedom AAXFILE [COVERJPG]
# 
# You can optionally specify a .jpg path either from your local machine or URL
# to use for the cover art.
#
# There does not appear to be an Audible player/downloader for Linux, so I use
# the Audible Android app to download the AAX file and copy it to my machine.
#
# Initial script from:
# https://www.reddit.com/r/linux/comments/5g841u/what_are_some_scripts_you_use_for_personal/daro100/
# heavily modified by John Wesorick
#
if [ -e "$PWD/bytes.txt" ]; then
    ABYTES=$(sed '1q;d' "$PWD/bytes.txt")
else
    echo "Activation bytes file (bytes.txt) is missing! Please use"
    echo "audible-activator (https://github.com/inAudible-NG/audible-activator/)"
    echo "to get your activation bytes and save them to a file called bytes.txt."
    exit 1
fi

# Verify that the necessary programs are installed
REQUIREDPROGRAMS=("ffmpeg" "lame" "mkvmerge")
for i in "${REQUIREDPROGRAMS[@]}"; do
    if ! type "$i" >/dev/null; then
        echo "Required program $i is missing! I cannot continue"
        # Check distro to see how to install requirements
        DISTRO=$(cat /etc/issue)
        if [[ "$DISTRO" == "Ubuntu"* ]]; then
            echo "Run the following command(s) to install all required programs"
            if [[ "$DISTRO" == *"14.04"* ]]; then
                echo "sudo add-apt-repository ppa:mc3man/trusty-media"
                echo "sudo apt-get update"
            fi
            echo "sudo apt-get install mkvtoolnix ffmpeg lame"
        fi
        exit 1
    fi
done
# Move into temporary work folder.
mkdir work
cd work

# Remove DRM from AAX file.
echo "Stripping DRM..."
ffmpeg -nostats -loglevel 0 -activation_bytes $ABYTES -i "../$1" \
    -vn -c:a copy "${1%.*}.m4a" ; 
BOOKTITLE=$(ffprobe -v quiet -show_format "${1%.*}.m4a" | grep "TAG:title" | cut -d"=" -f2)
AUTHOR=$(ffprobe -v quiet -show_format "${1%.*}.m4a" | grep "TAG:artist" | cut -d"=" -f2)
YEAR=$(ffprobe -v quiet -show_format "${1%.*}.m4a" | grep "TAG:date" | cut -d"=" -f2)
COMMENT=$(ffprobe -v quiet -show_format "${1%.*}.m4a" | grep "TAG:comment" | cut -d"=" -f2)
TOKENWORDS=("A" "An" "The")
# If a title begins with A, An, or The, we want to rename it so it sorts well
FSBOOKTITLE="$BOOKTITLE"
FSAUTHOR="$AUTHOR"
for i in "${TOKENWORDS[@]}"; do
    if [[ "$FSBOOKTITLE" == "$i "* ]]; then
        FSBOOKTITLE=$(echo $FSBOOKTITLE | perl -pe "s/^$i //")
        # If book has a subtitle, we want the token word to go right before it
        if [[ "$FSBOOKTITLE" == *": "* ]]; then
            FSBOOKTITLE=$(echo $FSBOOKTITLE | perl -pe "s/: /, $i: /")
            break  
        fi
        FSBOOKTITLE="$FSBOOKTITLE, $i"
        break
    fi
done
# Replace special characters in Book Title and Author Name with a - to make
# them file name safe. I'm not actually using the Author Name in the file
# name, but I figured it'd be nice to make it easy to use.
FSBOOKTITLE=$(echo $FSBOOKTITLE | perl -pe 's/[<>:"\/\\\|\?\*]/-/g')
FSAUTHOR=$(echo $FSAUTHOR | perl -pe 's/[<>:"\/\\\|\?\*]/-/g')

# Split stripped M4A file by chapters.
echo "Splitting by chapter..."
mkvmerge -o "${1%.*} .m4a" --split chapters:all "${1%.*}.m4a" 1> /dev/null

rm "${1%.*}.m4a"

# if album art is given as a second argument, grab it
if [ -n "$2" ]; then
    if [[ "$2" == "http"* ]]; then
        wget -q "$2" -O folder.jpg
    else
        mv "../$2" folder.jpg
    fi
fi

# Transcode to .mp3
TOTALTRACKS=$(ls -l *.m4a | wc -l)
TRACK=0

for f in *.m4a ;
do
    TRACK=$(($TRACK+1))
    echo "Transcoding track $TRACK of $TOTALTRACKS..."
    MP3FILE="${f//m4a/mp3}"
    TRACKTITLE="$(mkvinfo "$f" | grep 'ChapterString:' | cut -d":" -f2 | awk '{gsub(/^ +| +$/,"")} {print $0 }')"
    ffmpeg -nostats \
        -loglevel 0 \
        -i "$f" \
        -metadata album="$BOOKTITLE" \
        -metadata genre="Audiobook" \
        -metadata title="$TRACKTITLE" \
        -metadata artist="$AUTHOR" \
        -metadata album_artist="$AUTHOR" \
        -metadata date="$YEAR" \
        -metadata comment="$COMMENT" \
        -metadata track="$TRACK/$TOTALTRACKS" \
        -id3v2_version 3 \
        -codec:a libmp3lame \
        -qscale:a 3 \
        "$MP3FILE"
    # zero padding the track number as a string
    if [ $TOTALTRACKS -ge 100 ] ; then
        if [ $TRACK -le 99 ] ; then
            TRACKSTR="0$TRACK"
        elif [ $TRACK -le 9 ] ; then
            TRACKSTR="00$TRACK"
        else
            TRACKSTR=$TRACK
        fi
    else
        if [ $TRACK -le 9 ] ; then
            TRACKSTR="0$TRACK"
        else
            TRACKSTR=$TRACK
        fi
    fi
    
    # if album art exists, add it
    if [ -a "folder.jpg" ]; then
        # ffmpeg does not allow in-place editing, so create a temp file, then
        # delete the old one and rename.
        ffmpeg -nostats \
            -loglevel 0 \
            -i "$MP3FILE" \
            -i folder.jpg \
            -map 0:0 \
            -map 1:0 \
            -c copy \
            -id3v2_version 3 \
            -metadata:s:v title="Album cover" \
            -metadata:s:v comment="Cover (Front)" \
            out.mp3
        rm "$MP3FILE"
        mv out.mp3 "$MP3FILE"
    fi
    # rename .mp3's. Format: BOOKTITLE@TRACKNUM)TRACKTITLE.mp3
    mv "$MP3FILE" "$FSBOOKTITLE@$TRACKSTR)$TRACKTITLE.mp3"
done

# Move out of work folder.
mkdir "../$FSBOOKTITLE"
mv *.mp3 "../$FSBOOKTITLE" 
if [ -a folder.jpg ]; then
    mv folder.jpg "../$FSBOOKTITLE"
fi
cd ..

# Delete work folder.
echo "Cleaning up..."
rm -rf work

echo

The output:

Stripping DRM...
Splitting by chapter...
rm: cannot remove 'myaudiobook.m4a': No such file or directory
ls: cannot access '*.m4a': No such file or directory
Transcoding track 1 of 0...
rm: cannot remove '*.mp3': No such file or directory
mv: cannot stat 'out.mp3': No such file or directory
mv: cannot stat '*.mp3': No such file or directory
mv: cannot stat '*.mp3': No such file or directory
Cleaning up...

I thought it might be something wrong with the variables, so I tried wrapping some of them in with double quotes ("), but that didn't seem to solve the issue.

NB! Before anyone should get any ideas, I have purchased and, therefore, own these audiobooks.


EDIT: here are the results of the command bash -x audiblefreedom

$ bash -x audiblefreedom 
+ '[' -e /home/[REDACTED]/Music/audible/audiobooks/fry_stephen-mythos/bytes.txt ']'
++ sed '1q;d' /home/[REDACTED]/Music/audible/audiobooks/fry_stephen-mythos/bytes.txt
+ ABYTES=audible_byte#4-4_0_10000x789935_0.rtc
+ REQUIREDPROGRAMS=("ffmpeg" "lame" "mkvmerge")
+ for i in "${REQUIREDPROGRAMS[@]}"
+ type ffmpeg
+ for i in "${REQUIREDPROGRAMS[@]}"
+ type lame
+ for i in "${REQUIREDPROGRAMS[@]}"
+ type mkvmerge
+ mkdir work
+ cd work
+ echo 'Stripping DRM...'
Stripping DRM...
+ ffmpeg -nostats -loglevel 0 -activation_bytes audible_byte#4-4_0_10000x789935_0.rtc -i ../ -vn -c:a copy .m4a
++ ffprobe -v quiet -show_format .m4a
++ grep TAG:title
++ cut -d= -f2
+ BOOKTITLE=
++ ffprobe -v quiet -show_format .m4a
++ grep TAG:artist
++ cut -d= -f2
+ AUTHOR=
++ ffprobe -v quiet -show_format .m4a
++ grep TAG:date
++ cut -d= -f2
+ YEAR=
++ ffprobe -v quiet -show_format .m4a
++ grep TAG:comment
++ cut -d= -f2
+ COMMENT=
+ TOKENWORDS=("A" "An" "The")
+ FSBOOKTITLE=
+ FSAUTHOR=
+ for i in "${TOKENWORDS[@]}"
+ [[ '' == \A\ * ]]
+ for i in "${TOKENWORDS[@]}"
+ [[ '' == \A\n\ * ]]
+ for i in "${TOKENWORDS[@]}"
+ [[ '' == \T\h\e\ * ]]
++ echo ''
++ perl -pe 's/[<>:"\/\\\|\?\*]/-/g'
+ FSBOOKTITLE=
++ echo ''
++ perl -pe 's/[<>:"\/\\\|\?\*]/-/g'
+ FSAUTHOR=
+ echo 'Splitting by chapter...'
Splitting by chapter...
+ mkvmerge -o _.m4a --split chapters:all .m4a
+ rm .m4a
rm: cannot remove '.m4a': No such file or directory
+ '[' -n '' ']'
++ ls -l '*.m4a'
++ wc -l
ls: cannot access '*.m4a': No such file or directory
+ TOTALTRACKS=0
+ TRACK=0
+ for f in *.m4a
+ TRACK=1
+ echo 'Transcoding track 1 of 0...'
Transcoding track 1 of 0...
+ MP3FILE='*.mp3'
++ mkvinfo '*.m4a'
++ grep ChapterString:
++ cut -d: -f2
++ awk '{gsub(/^ +| +$/,"")} {print $0 }'
+ TRACKTITLE=
+ ffmpeg -nostats -loglevel 0 -i '*.m4a' -metadata album= -metadata genre=Audiobook -metadata title= -metadata artist= -metadata album_artist= -metadata date= -metadata comment= -metadata track=1/0 -id3v2_version 3 -codec:a libmp3lame -qscale:a 3 '*.mp3'
+ '[' 0 -ge 100 ']'
+ '[' 1 -le 9 ']'
+ TRACKSTR=01
+ '[' -a folder.jpg ']'
+ mv '*.mp3' '@01).mp3'
mv: cannot stat '*.mp3': No such file or directory
+ mkdir ../
mkdir: cannot create directory ‘../’: File exists
+ mv '*.mp3' ../
mv: cannot stat '*.mp3': No such file or directory
+ '[' -a folder.jpg ']'
+ cd ..
+ echo 'Cleaning up...'
Cleaning up...
+ rm -rf work
+ echo

How do I know which commands to run manually?

6
  • I'd be looking at the first ffmpeg or the subsequent mkvmerge. Run the script with -x tracing to see what the commands actually are, and then run them by hand to check they produce expected output Commented Aug 5, 2022 at 7:11
  • After reading mkvmerge manpage, I am left surprised by the space introduced before .m4a in the specification for the -o parameter mkvmerge -o "${1%.*} .m4a"
    – MC68020
    Commented Aug 5, 2022 at 7:27
  • @MC68020 so was I but I think it's the output file name to differentiate it from the input. Me, I wouldn't have used a space but an underscore or even the string "_chapter" Commented Aug 5, 2022 at 7:29
  • @roaima Please see my edit. Sorry, but I had forgotten about this.
    – telometto
    Commented Aug 8, 2022 at 6:09
  • Could be easier to 1/ cp audiblefreedom audiblefreedom_test 2/ edit audiblefreedom_test and delete all the lines from the one starting with BOOKTITLE=$ (all the lines following ffmpeg instruction. 3/ save and run that script 4/ report what happens (what files have been created in the work directory, size…) If nothing then retry after increasing loglevel in ffmpeg instruction and report here. If everything OK, we will start worrying about mkvmerge.
    – MC68020
    Commented Aug 8, 2022 at 6:27

1 Answer 1

1

First point of failure is the script's ffmpeg command.

You can see after the -i that the value for "${1}" is not reported in your -x output. There is no loop in the script that is using the set command to define ${1} from inside the script, so that implies that $1 must be the filename that is expected to be provided as a parameter when the script is called. Make sure that name is wrapped in double-quotes if there are any spaces or special characters.

Because $1 is not defined, the "${1%.*}.m4a" fails at every ffprobe call.

In other words, the script has not been told what file to work on.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .