Skip to content

Instantly share code, notes, and snippets.

@r15ch13
Last active December 18, 2023 18:45
Show Gist options
  • Star 19 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save r15ch13/0c548be006431607bf1eaecefdc0591a to your computer and use it in GitHub Desktop.
Save r15ch13/0c548be006431607bf1eaecefdc0591a to your computer and use it in GitHub Desktop.
Convert aax to m4a

Convert aax to m4a

Converts Audible aax audiobooks to m4a while keeping chapters and the cover image intact. The cover is added as a looped image so it is shown while playing on Plex Media Server

Also available as NodeJS package: https://github.com/r15ch13/audible-converter

Usage

> convert-aax-to-m4a.cmd <filename> <device> <debug>

  • <filename>: is your aax file. duh!
  • <device>: is the number of the registry entry in HKLM\SOFTWARE\WOW6432Node\Audible\SWGIDMAP.
  • <debug>: add debug as 3rd parameter to get more ffmpeg output

Example:

> convert-aax-to-m4a.cmd AgenttotheStars_ep6_A1SZN9UBXNTEA2.aax

Using Device 0 (Activation Bytes: 74FF0506)

Reading audiobook information ...
John Scalzi - Agent to the Stars (Unabridged) [2010] (Duration: 8h44m)

Converting to m4a ...
size=  246321kB time=08:49:45.00 bitrate=  63.5kbits/s speed=7.09e+003x

Adding looped cover image audiobook
frame=31844 fps=645 q=17.0 Lsize=  256060kB time=08:49:45.00 bitrate=  66.0kbits/s speed= 644x

Done!

Requirements

  • Installed Audible Download Manager
  • ffmpeg for converting
  • jq for extracting information

How it works

  • Extract cover image:
    • ffmpeg -y -i audiobook.aax cover.png
  • Decrypted and convert to m4a:
    • ffmpeg -y -activation_bytes 1CEB00DA -i audiobook.aax -c:a copy -vn audiobook-tmp.m4a
  • Add looped cover image:
    • ffmpeg -y -r 1 -loop 1 -i cover.png -i audiobook-tmp.m4a -c:a copy -shortest audiobook.m4a

License

The MIT License (MIT)

@echo off
setlocal enableextensions
chcp 65001
set input=%1
set device=%2
set loglevel=quiet
for %%i in ("%~f1") do set dirname=%%~dpi
for %%i in ("%~f1") do set extension=%%~xi
set tempfile=%dirname%%RANDOM%-temp.m4a
set tempimage=%dirname%%RANDOM%-temp.png
if /i "%1" EQU "" (echo Usage: %0 ^<filename^> ^<device^> & goto :eof)
if /i "%extension%" NEQ ".aax" (echo Not an AAX file! & goto :eof)
if /i "%2" EQU "" set device=0
if /i "%3" EQU "debug" set loglevel=info
where /q ffmpeg || (echo Please install ffmpeg! & goto :eof)
where /q jq || (echo Please install jq! & goto :eof)
echo.
rem http://stackoverflow.com/a/12730022/2710739
for /f "tokens=2*" %%a in ('reg.exe query "HKLM\SOFTWARE\WOW6432Node\Audible\SWGIDMAP" /v "%device%"') do set bytes=%%b
set activation_bytes=%bytes:~6,2%%bytes:~4,2%%bytes:~2,2%%bytes:~0,2%
if /i "%bytes%" EQU "" echo Device %device% does not exist! & goto :eof
if /i "%activation_bytes%" EQU "FFFFFFFF" echo Device %device% is not activated! & goto :eof
echo Using Device %device% (Activation Bytes: %activation_bytes%)
echo.
echo Reading audiobook information ...
for /f "tokens=*" %%i in ('ffprobe "%input%" -v %loglevel% -print_format json -show_format ^| jq -r ".format.tags.artist"') do set artist=%%i
for /f "tokens=*" %%i in ('ffprobe "%input%" -v %loglevel% -print_format json -show_format ^| jq -r ".format.tags.title"') do set title=%%i
for /f "tokens=*" %%i in ('ffprobe "%input%" -v %loglevel% -print_format json -show_format ^| jq -r ".format.tags.date"') do set year=%%i
for /f "tokens=*" %%i in ('ffprobe "%input%" -v %loglevel% -print_format json -show_format ^| jq -r """\(.format.duration|(tonumber/60/60)|floor)h\(.format.duration|(tonumber%%60))m"""') do set duration=%%i
set filename=%artist% - %title% [%year%].m4a
set filename=%filename:\=%
set filename=%filename:/=%
set filename=%filename::=%
set filename=%filename:?=%
set filename=%filename:"=%
set filename=%filename:<=%
set filename=%filename:>=%
set filename=%filename:|=%
rem crashes the cmd if variables contain umlauts ...
rem echo %artist% - %title% [%year%] (Duration: %duration%)
echo.
echo Converting to m4a ...
ffmpeg -y -v quiet -i "%input%" "%tempimage%"
ffmpeg -y -v %loglevel% -stats -activation_bytes %activation_bytes% -i "%input%" -c:a copy -vn "%tempfile%" || (echo Audiobook can't be unlocked with this Activation Bytes: %activation_bytes% & goto :cleanup)
echo.
echo Adding looped cover image audiobook
ffmpeg -y -v %loglevel% -stats -r 1 -loop 1 -i "%tempimage%" -i "%tempfile%" -c:a copy -shortest "%dirname%%filename%"
echo.
echo Done!
goto :cleanup
:cleanup
del /q "%tempfile%" >nul 2>&1
del /q "%tempimage%" >nul 2>&1
goto :eof
@ryanrk
Copy link

ryanrk commented Jul 29, 2016

Does this copy over the chapter information?

@r15ch13
Copy link
Author

r15ch13 commented Dec 11, 2016

@ryanrk yes it does
the heart of this thing is: ffmpeg -activation_bytes <activation_bytes> -i <input>.aax -c:a copy -vn <output>.m4a

@thevodkaboy
Copy link

thevodkaboy commented Feb 27, 2018

I'm having a hard time using the simple command "convert-aax-to-m4a.cmd (filename) (device)"

  • I do have my activation bytes. i had to retrieve them manually.
  • My Reg doesn't seem to have the activation bytes listed in the registry location and gives me an error, that device 0 doesn't exist.
  • I can run through the heart of the code and manually run each ffmpeg commands, but I'm missing all of my chapters.
  • I really don't want to start editing my Reg, so i feel like i missed something.

Thoughts?

@meoow
Copy link

meoow commented Apr 5, 2018

ffmpeg does not yet have the ability to embed cover image into MP4 container with major_brand of M4A. the command ffmpeg -y -r 1 -loop 1 -i cover.png -i audiobook-tmp.m4a -c:a copy -shortest audiobook.m4a will actually convert the file from M4A to M4V which is a mp4 video file. Some music player devices may fail to recognize such file type.

ffmpeg -i test1.m4a

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test1.m4a':
  Metadata:
    major_brand     : M4A
    minor_version   : 512
    compatible_brands: isomiso2
    title           : 白き魔女
    artist          : Falcom Sound Team jdk
    album_artist    : Falcom Sound Team jdk
    album           : 交響幻想曲「白き魔女」
    encoder         : Lavf57.83.100
    track           : 1/10
  Duration: 00:06:37.81, start: 0.000000, bitrate: 743 kb/s
    Stream #0:0(und): Audio: alac (alac / 0x63616C61), 44100 Hz, stereo, s16p, 742 kb/s (default)
    Metadata:
      handler_name    : SoundHandler
At least one output file must be specified
ffmpeg -r 1 -loop 1 -i front.jpg -i test1.m4a -c:a copy -shortest test2.m4a
ffmpeg -i test2.m4a

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'test2.m4a':
  Metadata:
    major_brand     : M4V
    minor_version   : 512
    compatible_brands: isomiso2avc1
    encoder         : Lavf57.83.100
  Duration: 00:06:41.00, start: 0.000000, bitrate: 739 kb/s
    Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuvj420p(pc), 300x300 [SAR 1:1 DAR 1:1], 1 kb/s, 1 fps, 1 tbr, 16384 tbn, 2 tbc (default)
    Metadata:
      handler_name    : VideoHandler
    Stream #0:1(und): Audio: alac (alac / 0x63616C61), 44100 Hz, stereo, s16p, 742 kb/s (default)
    Metadata:
      handler_name    : SoundHandler
At least one output file must be specified

You should use mp4art from mp4box instead.

@GuardedLegacy
Copy link

So I'll start by admitting I'm not too smart on all this. I created a GitHub account in order to comment here if that shows how new I am.
First, I couldn't run this from PowerShell--couldn't seem to find ffmpeg, even after adding to Path variable. This could just be my limited PowerShell knowledge, but I did get this to run in cmd. Maybe I have to restart PS for it to accept the updated Path variable. Also as a note on on the functionality of "where", when I downloaded jq, it was named jq-win64.exe by default, but your script didn't find it until I renamed it jq.exe. I'd suggest adding a comment that it might need to be renamed. I just guessed that might be what I needed to do.
Second, and my primary reason for wanting to comment, the script worked fine for a file with no spaces in the filename, but when I ran it on files with spaces, it gave me the error "[first word after space] was unexpected at this time." The filename was in double quotes when I ran the command, so I was surprised by this error. Have you encountered this? I don't know enough to fix this in the script for myself, so I'll just have to remove all space from the filenames.
Finally, would it be possible to add an option to bypass checking the registry key for a device and just input the activation_bytes manually? I think I can figure out how to comment all this out and just assign the activation bytes in the script. Not that this is a required function, I only noticed because the initial file I tested the script on was a book my mom had shared with me, and it wasn't from my audible account, so the activation_bytes were different. Additionally, I had already gone through the process of digging up my activation_bytes before I realized this script did it for me.
Thanks for the useful script.

@LinusU
Copy link

LinusU commented Jan 3, 2019

Update for FFMPEG 4.1

FFMPEG now natively supports copying the Album Art cover, just drop the -vn flag and you can get a fully working m4b file with album art and all metadata intact, with just one command:

ffmpeg -activation_bytes 00112233 -i MyNiceBook.aax -c copy MyNiceBook.m4b

@LinusU
Copy link

LinusU commented Jun 28, 2019

Get activation key from windows registry

If you have the Audible Download manager installed on your windows computer you can get the activation key/authcode from the windows registry. Navigate to HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Audible\SWGIDMAP and look for one of lines that does not start with ff ff ff ff. The first four hex-pairs are the code in reverse, note them down and use pass them to ffmpeg.

@JKamsker
Copy link

Get the activation key from the aax itself: https://audible-tools.github.io/

@kdk24
Copy link

kdk24 commented Oct 27, 2023

Get the activation key from the aax itself: https://audible-tools.github.io/

Thank you very much for the audible-tools link. I spent hours trying to figure out how to convert my one and only legally purchased Audible audio book to .mp3 so I could listen to it other than through Audible.

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