172 Commits
v1.1 ... v1.2

Author SHA1 Message Date
70ae2cef12 Merge pull request #153 from Nicko98/master
Fix for issue #145
2021-02-21 18:23:29 -05:00
9058cf144e Fix for KrumpetPirate/AAXtoMP3 issue #145
This reverses pull request #139 if ffmpeg version is below 4.
2021-02-21 23:17:17 +01:00
1e9c42aa83 Merge pull request #151 from fabh2o/custom-output-folder
Custom naming scheme
2021-02-11 19:31:39 -05:00
82c0ef0f6b Merge branch 'master' of github.com:KrumpetPirate/AAXtoMP3 into custom-output-folder 2021-02-11 09:26:23 +01:00
c3584923ca Merge pull request #148 from fabh2o/keep-n-author
Author customization
2021-02-10 17:42:38 -05:00
f6661862de mediainfo install in README 2021-02-10 21:22:46 +01:00
b5408310a2 Merge branch 'fix-log-level-time-as-octal' of github.com:fabh2o/AAXtoMP3 into custom-output-folder 2021-02-10 21:08:23 +01:00
3c909baf5e Merge branch 'master' of github.com:KrumpetPirate/AAXtoMP3 into custom-output-folder 2021-02-10 20:59:48 +01:00
012749071e force base-10 2021-02-10 20:48:14 +01:00
36839646c4 deleted empty line 2021-02-10 09:26:25 +01:00
68168f6495 Merge branch 'master' into keep-n-author 2021-02-10 09:20:37 +01:00
aaaea1515f Merge pull request #150 from Nicko98/loglevels
Loglevels
2021-02-09 16:02:29 -05:00
836c6b0722 use 2021-02-09 20:55:38 +01:00
4a8df563eb fix short flags 2021-02-09 20:53:53 +01:00
184cece622 fix space 2021-02-09 20:49:44 +01:00
1c4d67202a fix space 2021-02-09 20:49:12 +01:00
93478c797d Merge branch 'master' of github.com:KrumpetPirate/AAXtoMP3 into custom-output-folder 2021-02-09 20:47:34 +01:00
6646b84e72 Added different loglevels
I added different loglevels and (for loglevel 0 and 1) a progress bar for chapter splitting.
0 = show progress only, chapter splitting shown as progress bar
1 (default) = fewer information, chapter splitting shown as progress bar
2 = as it has been up until now
3 = debug mode
2021-02-09 20:21:55 +01:00
2805aca990 Added different loglevels
I added different loglevels and (for loglevel 0 and 1) a progress bar for chapter splitting.
0 = show progress only, chapter splitting shown as progress bar
1 (default) = fewer information, chapter splitting shown as progress bar
2 = as it has been up until now
3 = debug mode
2021-02-09 20:19:53 +01:00
7bef1bd569 Readme updated for loglevels 2021-02-09 20:05:43 +01:00
9e1b33904a Added different loglevels
I added different loglevels and (for loglevel 0 and 1) a progress bar for chapter splitting.
0 = show progress only, chapter splitting shown as progress bar
1 (default) = fewer information, chapter splitting shown as progress bar
2 = as it has been up until now
3 = debug mode
2021-02-09 20:00:11 +01:00
fb48899cb7 precedence 2021-02-09 19:03:55 +01:00
239ae1aa3f remove leading space after cut 2021-02-09 18:54:37 +01:00
fcef788bc4 artist --> author 2021-02-09 17:48:21 +01:00
33a8d254bd Merge branch 'master' of github.com:KrumpetPirate/AAXtoMP3 into keep-n-author 2021-02-09 17:40:43 +01:00
de3fbadab6 Merge pull request #147 from Nicko98/faster-chapter-splitting
Faster chapter splitting
2021-02-09 10:11:13 -05:00
e6f34b5f97 faster chapter splitting
Since there is an issue with this fix when applied on flac option, the changes dont take effect on flac.
For more information have a look in KrumpetPirate/AAXtoMP3 issue #142.
2021-02-09 12:39:15 +01:00
2b31c7defa faster chapter splitting
Since there is an issue with this fix when applied on flac option, the changes dont take effect on flac.
For more information have a look in KrumpetPirate/AAXtoMP3 issue #142.
2021-02-09 03:24:20 +01:00
c4c5470b6b faster chapter splitting
Since there is an issue with this fix when applied on flac option, the changes dont take effect on flac.
For more information have a look in KrumpetPirate/AAXtoMP3 issue #142.
2021-02-09 03:15:22 +01:00
1e7802ac3f force author name 2021-02-08 22:04:32 +01:00
ad2bbe5da4 typo in sed 2021-02-08 21:46:57 +01:00
0a5813e7db rephrased flag description 2021-02-08 21:39:10 +01:00
ad86f187f8 keep n-th artist only 2021-02-08 21:29:26 +01:00
ffbc592bf8 correct playlist name? 2021-02-07 11:32:18 +01:00
8f0f8ec3e1 forgot ; 2021-02-07 11:24:23 +01:00
7b602eaf5d proper multi file support i hope 2021-02-07 11:21:07 +01:00
bcaec020ce first commit 2021-02-06 22:27:52 +01:00
440c6302fb Merge pull request #144 from fabh2o/mediainfo
Mediainfo tags
2021-02-05 20:52:19 -05:00
3fb377a107 debug var publisher 2021-02-05 16:13:06 +01:00
ce3b5a5c18 Merge pull request #141 from Nicko98/master
Added compression levels for mp3, opus and flac
2021-02-05 08:58:52 -05:00
4870dc334a use publisher tag 2021-02-05 10:42:14 +01:00
712b69d8ec get metadata value 2021-02-05 10:39:00 +01:00
7be5ee8749 extract only some tags, not all 2021-02-05 10:37:23 +01:00
d11257410b Added remark, that quality can't be increased.
Added a remark, that quality can't be increased over the quality of the original file.
2021-02-05 01:09:51 +01:00
8a82d9f661 Merge pull request #143 from fabh2o/exit-if-no-aax-found
Exit if no aax file is found
2021-02-04 07:46:57 -05:00
db65efaf67 Added Compression levels for mp3, opus and flac 2021-02-04 11:20:13 +01:00
0c9fa05aab return 1 if no aax file found 2021-02-04 10:09:33 +01:00
fd8a51d474 Added Compression levels for mp3, opus and flac 2021-02-04 01:06:33 +01:00
cbf75faa58 Added Compression levels for mp3, opus and flac 2021-02-04 01:02:03 +01:00
d56369d45a Merge pull request #140 from Nicko98/master
Updated README since chaptered mode works for flac as well
2021-02-03 10:01:58 -05:00
277bbaec13 Add mediainfo as optional requirement to README 2021-02-03 09:09:20 -05:00
84fb5bf004 Merge pull request #138 from fabh2o/master
Use mediainfo to parse additional metadata
2021-02-03 09:06:16 -05:00
82b865f55f comments about the reading of chapter infos 2021-02-03 14:24:02 +01:00
87a7a53352 updated README since chaptered flac works
--chaptered does work for flac as well, maybe my last pull-request fixed this.
2021-02-03 14:18:52 +01:00
c0fa0489cd updated README, since chaptered flac works
--chaptered does work for flac as well, maybe my last pull-request fixed this.
2021-02-03 14:14:20 +01:00
d16a47049b Merge branch 'master' into master 2021-02-03 11:07:56 +01:00
3b4648519b Merge pull request #139 from Nicko98/master
Way faster chapter splitting
2021-02-02 18:29:46 -05:00
ffb7e40961 Way faster chapter splitting
Just a little change in the ffmpeg call, but this massively increases the speed of chapter splitting. (In my first test approximately by a factor of 10.)
2021-02-02 23:01:23 +01:00
dc5309ebc3 Way faster chapter splitting
Just a little change in the ffmpeg call, but this massively increases the speed of chapter splitting. (In my first test approximately by a factor of 10.)
2021-02-02 22:56:09 +01:00
9a674d0127 Use mediainfo to parse additional metadata 2021-02-02 10:53:10 +01:00
9e3a2a3787 Merge pull request #137 from Gelma/typo_28
Typo 28
2021-01-28 15:28:12 -05:00
9989aa5ad6 Add note about package install on Ubuntu 20.04
gelma@gopt:/tmp/AudioLibro$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 20.04.1 LTS
Release:        20.04
Codename:       focal

gelma@gopt:/tmp/AudioLibro$ sudo apt-get install ffmpeg libav-tools x264 x265 bc
Reading package lists... Done
Building dependency tree
Reading state information... Done
Package libav-tools is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source
However the following packages replace it:
  ffmpeg:i386 ffmpeg

E: Package 'libav-tools' has no installation candidate
2021-01-28 19:05:00 +01:00
b9f21a54f1 Fix typos 2021-01-28 19:01:38 +01:00
a6f19f8aa1 Merge pull request #136 from Nicko98/master
Continue after interruption
2021-01-26 19:58:39 -05:00
d007e21227 Added --continue to README.md 2021-01-26 23:44:05 +01:00
32aacd2b42 Continue after interrupt
Until now, when the conversion of a audiobook gets interrrupted during the splitting phase, one has to delete all the output files and restart from beginning.
This can be very uncomfy, especially if it's a long audiobook and the computer did work on it for several hours.
I added some changes, so that one only has to delete the last (incompletely generated) chapter and add "--continue " to the command where CHAPTERNUMBER is the chapter, that has been interrupted while splitting of.
For this to work I mainly just skipped the steps which were already done before interruption with if-conditions.
2021-01-26 23:32:14 +01:00
c4f2da205a Merge pull request #134 from andreasziegl/patch-1
Print total chapter count when extracting chapters
2021-01-26 16:30:11 -05:00
04b7d9ff45 Print total chapter count when extracting chapters 2021-01-18 11:11:51 +01:00
2ef05cda00 Merge pull request #129 from NicoVIII/master
Remove chapter metadata in "chaptered mode"
2020-07-14 18:46:07 -04:00
84d708f217 Remove chapters in "chaptered mode" 2020-07-14 10:21:26 +02:00
60bf8b0d87 Merge pull request #122 from Gelma/typo
Fix typos
2020-04-17 19:59:35 -04:00
624ca434e0 Fix typos 2020-04-17 10:11:51 +02:00
71f54b35e5 Merge pull request #121 from simcop2387/master
Small updates for Opus and metadata
2020-04-16 23:27:15 -04:00
a3ac4fb834 Small updates for Opus and metadata
Forcibly add the track and title metadata to the audio stream.  Fixes missing meta-data with Plex and possibly other programs.
Use the .opus file extension and ogg container for opus files to match recommended practice, and correct detection of opus files in Plex and other programs
2020-04-15 18:27:31 -07:00
640ba915a9 Merge pull request #119 from 73k05/master
Fix mkdir: cannot create directory : File name too long
2020-03-05 20:14:52 -05:00
2d5902ed6e Fix mkdir: cannot create directory : File name too long On linux file name max is 143 characters. Fix max name to 100 characters 2020-03-05 23:43:53 +01:00
1dfd8ae755 Default noclobber off 2019-10-06 00:00:54 -04:00
9f8cda5899 Merge pull request #112 from FreedomBen/no-clobber
Add no-clobber option to CLI
2019-10-01 18:08:35 -04:00
7b77b944cf Add no-clobber option to CLI
So the user can abort if the target directory would be overwritten.

Pork:  Remove trailing whitespace
2019-10-01 13:17:18 -06:00
7d7dd0f91c Merge pull request #105 from yzhernand/chaptered-mp4s
Create chaptered single-file m4a's/m4b's
2019-06-08 22:49:00 -04:00
3edb1b2fc8 Create chaptered single-file m4a's/m4b's
- Changes "container" for m4a and m4b to "mp4" as that is
the official container type.
- Changes "extension" checks for m4b to "container" checks
which check for "mp4". This makes it possible to produce
chaptered m4a files as well.
- Needs awk, but I think portable awk code is used. This
should probably be tested on other platforms, like macOS.
- Adds a check for mp4chaps, but that should be included
in the same package as mp4art.
- Only adds chapter data to mp4 files if using single-file
output mode.
2019-05-23 20:22:22 -04:00
93ea984a53 Merge pull request #102 from mithomas/master
Fixed minor doc typos.
2019-03-17 16:28:39 -04:00
29a6481664 Merge pull request #101 from javabrett/doc-fixes
Fixed minor doc-typos, some EOL whitespace. Fixed #95, fixed #96.
2019-03-17 16:28:28 -04:00
010dd7fb2d Fixed minor doc typos. 2019-03-17 16:07:11 +01:00
abafa6285c Fixed minor doc-typos, some EOL whitespace. Fixed #95, fixed #96. 2019-03-16 23:43:41 +11:00
5c2afd7d6c Merge pull request #99 from javabrett/add-bc-in-script-prompt
Also add bc to apt-get install list as prompted by script
2019-03-10 12:03:17 -04:00
243289c3ab Also add bc to apt-get install list as prompted by script. 2019-03-10 20:55:20 +11:00
847f5fb721 Merge pull request #93 from supremesyntax/fix/m3ufile
fix file extension in m3u file
2018-11-30 18:19:00 -06:00
13c240ab53 fix file extension in m3u file
Use file extension in m3u file rather than container format.
2018-11-30 08:50:13 +00:00
3c7a5f1ac1 Merge pull request #90 from gorillamoe/fix/readme-chaptered-usage-example
Fix --chaptered usage example
2018-10-03 19:19:27 -05:00
06d49e9f27 Fix --chaptered usage example
The arguments `-aac` and `-em4b` did not exist.
Just changed them to the according options: `--aac` and `-e:m4b`.
2018-10-03 16:22:07 +02:00
8b0f9a4989 Merge pull request #87 from pbowyer/patch-1
Add bc to Ubuntu package list
2018-08-27 18:09:13 -05:00
61cb5b43d8 Add bc to Ubuntu package list
On a stock Ubuntu 16.04 server install, bc is not present, and splitting into chapter files fails.
2018-08-27 20:42:51 +01:00
0d5f3b4098 Merge pull request #85 from upuv/master
README.md update to reflect recent changes in AAXtoMP3
2018-07-26 07:35:12 -05:00
b506b7a12a updated README.md to reflect changes. 2018-07-25 14:03:27 +10:00
09e82c0ed6 Merge pull request #5 from KrumpetPirate/master
Merge Krumpet Changes.
2018-07-25 13:58:23 +10:00
7a78db51a8 Merge pull request #84 from Jamstah/master
Fix bad argument name in usage
2018-07-23 11:02:33 -05:00
67a190b0a6 Fix bad argument name in usage 2018-07-22 21:46:15 +01:00
e9f06c8089 Merge pull request #82 from dehylton/osx-gsed
check for GNU sed, recommend gnu-sed for mac os x if missing
2018-07-02 19:06:57 -05:00
35308ccb11 check for GNU sed, recommend gnu-sed for mac os x if missing 2018-07-02 19:26:15 -04:00
4040fb7ab9 Merge pull request #79 from upuv/master
Big Pull Request # 2
2018-06-29 18:05:52 -05:00
a92705fbbf Forced merge 2018-06-25 14:44:53 +10:00
8de33e9c10 Merge pull request #3 from b0wter/fedora_install
Added up-to-date instructions for Fedora/RHEL/CentOS
2018-06-25 14:31:31 +10:00
a6da4412b8 Fixing Bad Merge. 2018-06-25 14:17:30 +10:00
3b66ef4443 Merge pull request #78 from KrumpetPirate/revert-57-master
Revert "Large Pull request"
2018-06-24 20:28:35 -05:00
89ab63a235 Revert "Large Pull request" 2018-06-24 20:28:09 -05:00
1c108acf4b Merge pull request #57 from upuv/master
Large Pull request
2018-06-24 20:10:34 -05:00
Joe
fab6141ccf Added up-to-date instructions for how to install ffmpeg on Fedora and
RHEL/CentOS.
2018-06-23 00:21:52 +02:00
6d2768613c Merge branch 'master' into master 2018-06-21 17:25:02 +10:00
101861f41c Fix for chapter length. 2018-06-21 17:21:18 +10:00
1af3f0ebcc Added checks for ffmpeg and ffprobe. 2018-06-21 17:07:47 +10:00
2f8bb35b68 Merge pull request #61 from reidar-cederqvist/master
AAXtoMP3: fix how .authcode is located fix issue #60
2018-06-13 08:10:44 -05:00
8ebd16ae8b AAXtoMP3: fix possition of dash when replacing colon 2018-06-07 09:26:38 +02:00
db48c6a970 AAXtoMP3: fix how .authcode is located fix issue #60 2018-06-07 09:11:47 +02:00
03f1a58638 Verify aax files options Improvements 2018-06-06 00:39:33 +10:00
9d20609f3c More Refactor, Setting up for user defined file naming using variable names. 2018-06-04 17:11:12 +10:00
71e140259b Refactor some variables for readability. 2018-06-04 16:10:00 +10:00
c2082ccfc0 Big change to DEBUG Output. It now handles lists of vars to dump. Makes the code a little cleaner. 2018-06-03 23:15:31 +10:00
de6d5db3f2 Enhanced DEBUG output for command line options 2018-06-03 15:49:19 +10:00
8327f15ac2 Adding --complete_dir option and documentation 2018-06-03 15:34:56 +10:00
1727d10be3 Fixed usage string 2018-05-26 16:23:12 +10:00
09145b4df2 Fixed an issue with container setting. 2018-05-26 16:16:00 +10:00
84335828d9 Bug Fix, added --chaptered, Readme updates. 2018-05-26 15:27:39 +10:00
eaf75eb2e2 Fixed a regression. m4a and m4b now have coverart. 2018-05-24 15:36:05 +10:00
11292dc4c4 Cleaned up some more Readme 2018-05-23 23:49:34 +10:00
6d1a751b81 Cleaned up some more Readme 2018-05-23 23:23:40 +10:00
1f2cd830d6 Woops left reference to m4b script in readme. 2018-05-23 23:19:36 +10:00
67d4d05b52 Added in support for m4b, Removed old m4b script. 2018-05-23 23:18:23 +10:00
6f343c9191 Merge branch 'master' into master 2018-05-21 22:19:51 +10:00
ac656f9723 Inserting code comments for the next guy. 2018-05-20 00:50:48 +10:00
f2c78da132 Tweeking Part 2 Readme.md 2018-05-20 00:25:47 +10:00
5e8cffd0ae Tweeking Readme.md 2018-05-20 00:22:14 +10:00
73f8b3c1da Woops angle brackets in Readme 2018-05-19 23:53:32 +10:00
fdf9a08f67 Merge branch 'master' of https://github.com/upuv/AAXtoM4B 2018-05-19 23:47:59 +10:00
c3b53b6e56 Merge pull request #2 from upuv/tiddy
Tiddy merge Master
2018-05-19 23:47:30 +10:00
5cca255ae5 Merge branch 'tiddy' of https://github.com/upuv/AAXtoM4B 2018-05-19 23:42:41 +10:00
e848404d06 Merge branch 'master' of https://github.com/upuv/AAXtoM4B 2018-05-19 23:41:26 +10:00
e754e83b6b Added big FAT metadata to DEBUG output. 2018-05-19 23:35:11 +10:00
cd60926a58 Merge branch 'tiddy' of https://github.com/upuv/AAXtoM4B into tiddy 2018-05-19 23:12:16 +10:00
2ac1ba67ec remove redundant AAXtoM4A 2018-05-19 23:09:38 +10:00
b952cab104 Nuked redundant m4a script 2018-05-19 23:08:11 +10:00
0efe3fa28e Redundant AAXtoMP3 now does this. 2018-05-19 23:07:04 +10:00
5ec1fcd2d9 Updated .gitignore
Attempting to avoid any accidental uploading to github of copyright material that only the user owns.
2018-05-19 23:04:08 +10:00
66dc8953ad Enhanced debug flag. 2018-05-19 23:01:34 +10:00
e69b212982 diferentiated between a log and debug message 2018-05-19 21:29:20 +10:00
aa1509b6ca Merge pull request #1 from upuv/getopts
Getopts
2018-05-19 16:06:16 +10:00
ccfaf07021 some cleanup 2018-05-19 15:52:38 +10:00
dee0d7e9a0 some cleanup 2018-05-19 15:51:40 +10:00
41a2803e18 some cleanup 2018-05-19 15:48:08 +10:00
10ca1e53e3 some cleanup 2018-05-19 15:41:23 +10:00
f976940798 getopts update 2018-05-19 15:08:57 +10:00
710ce0dfbe Merge pull request #2 from KrumpetPirate/master
merge upstream
2018-04-18 16:34:02 +08:00
8887bee900 Merge pull request #55 from thunder-hammer/patch-1
Corrected usage spelling
2018-04-16 17:12:33 -05:00
6ed19fcde2 Update AAXtoMP3
Corrected spelling in usage prompt
2018-04-16 16:09:47 -06:00
214dd5a46c Updating README 2018-02-20 23:21:19 +11:00
76baaac18a Updating README 2018-02-20 23:19:58 +11:00
e5d1588825 Updating README 2018-02-20 23:18:25 +11:00
639940a497 Updating README 2018-02-20 23:16:34 +11:00
004094f842 Updating README 2018-02-20 23:13:27 +11:00
30cbe5d0f0 Updating README 2018-02-20 23:12:00 +11:00
cd0e5546f4 Updating README 2018-02-20 23:10:29 +11:00
e5c45ed119 Updating README 2018-02-20 23:03:52 +11:00
cfaa5b94ff Updating README 2018-02-20 22:50:34 +11:00
5a7078bc99 Updating README 2018-02-20 22:43:54 +11:00
c4eb954e11 Updating README 2018-02-20 22:43:02 +11:00
8b391a0bd0 Updating README 2018-02-20 22:41:55 +11:00
d0eb3540ed Updating README 2018-02-20 22:30:34 +11:00
ab8db09eed Updating README 2018-02-20 22:27:52 +11:00
bacdde31e3 Merge pull request #1 from upuv/master
Added m3u playlist support
2018-02-20 11:04:05 +08:00
304594e845 Added m3u playlist support 2018-02-18 17:26:45 +11:00
bcd6ffd14b Convert aax to m4b 2017-11-22 16:31:42 +08:00
176c30c42c add m4a conversion script 2017-11-03 11:33:21 +08:00
2 changed files with 935 additions and 138 deletions

859
AAXtoMP3
View File

@ -1,157 +1,768 @@
#!/usr/bin/env bash
# ========================================================================
# Command Line Options
# Usage Synopsis.
usage=$'\nUsage: AAXtoMP3 [--flac] [--aac] [--opus ] [--single] [--level <COMPRESSIONLEVEL>]
[--chaptered] [-e:mp3] [-e:m4a] [-e:m4b] [--authcode <AUTHCODE>] [--no-clobber]
[--target_dir <PATH>] [--complete_dir <PATH>] [--validate] [--loglevel <LOGLEVEL>]
[--keep-author <N>] [--author <AUTHOR>] [--{dir,file,chapter}-naming-scheme <STRING>]
[--continue <CHAPTERNUMBER>] {FILES}\n'
codec=libmp3lame # Default encoder.
extension=mp3 # Default encoder extension.
level=-1 # Compression level. Can be given for mp3, flac and opus. -1 = default/not specified.
mode=chaptered # Multi file output
auth_code= # Required to be set via file or option.
targetdir= # Optional output location. Note default is basedir of AAX file.
dirNameScheme= # Custom directory naming scheme, default is $genre/$author/$title
customDNS=0
fileNameScheme= # Custom file naming scheme, default is $title
customFNS=0
chapterNameScheme= # Custom chapter naming scheme, default is '$title-$(printf %0${#chaptercount}d $chapternum) $chapter' (BookTitle-01 Chapter 1)
customCNS=0
completedir= # Optional location to move aax files once the decoding is complete.
container=mp3 # Just in case we need to change the container. Used for M4A to M4B
VALIDATE=0 # Validate the input aax file(s) only. No Transcoding of files will occur
loglevel=1 # Loglevel: 0: Show progress only; 1: default; 2: a little more information, timestamps; 3: debug
noclobber=0 # Default off, clobber only if flag is enabled
continue=0 # Default off, If set Transcoding is skipped and chapter splitting starts at chapter continueAt.
continueAt=1 # Optional chapter to continue splitting the chapters.
keepArtist=-1 # Default off, if set change author metadata to use the passed argument as field
authorOverride= # Override the author, ignoring the metadata
# -----
# Code tip Do not have any script above this point that calls a function or a binary. If you do
# the $1 will no longer be a ARGV element. So you should only do basic variable setting above here.
#
# Process the command line options. This allows for un-ordered options. Sorta like a getops style
while true; do
case "$1" in
# Flac encoding
-f | --flac ) codec=flac; extension=flac; mode=single; container=flac; shift ;;
# Apple m4a music format.
-a | --aac ) codec=copy; extension=m4a; mode=single; container=m4a; shift ;;
# Ogg Format
-o | --opus ) codec=libopus; extension=opus; container=ogg; shift ;;
# If appropriate use only a single file output.
-s | --single ) mode=single; shift ;;
# If appropriate use only a single file output.
-c | --chaptered ) mode=chaptered; shift ;;
# This is the same as --single option.
-e:mp3 ) codec=libmp3lame; extension=mp3; mode=single; container=mp3; shift ;;
# Identical to --acc option.
-e:m4a ) codec=copy; extension=m4a; mode=single; container=mp4; shift ;;
# Similar to --aac but specific to audio books
-e:m4b ) codec=copy; extension=m4b; mode=single; container=mp4; shift ;;
# Change the working dir from AAX directory to what you choose.
-t | --target_dir ) targetdir="$2"; shift 2 ;;
# Use a custom directory naming scheme, with variables.
-D | --dir-naming-scheme ) dirNameScheme="$2"; customDNS=1; shift 2 ;;
# Use a custom file naming scheme, with variables.
-F | --file-naming-scheme ) fileNameScheme="$2"; customFNS=1; shift 2 ;;
# Use a custom chapter naming scheme, with variables.
--chapter-naming-scheme ) chapterNameScheme="$2"; customCNS=1; shift 2 ;;
# Move the AAX file to a new directory when decoding is complete.
-C | --complete_dir ) completedir="$2"; shift 2 ;;
# Authorization code associate with the AAX file(s)
-A | --authcode ) auth_code="$2"; shift 2 ;;
# Don't overwrite the target directory if it already exists
-n | --no-clobber ) noclobber=1; shift ;;
# Extremely verbose output.
-d | --debug ) loglevel=3; shift ;;
# Set loglevel.
-l | --loglevel ) loglevel="$2"; shift 2 ;;
# Validate ONLY the aax file(s) No transcoding occurs
-V | --validate ) VALIDATE=1; shift ;;
# continue splitting chapters at chapter continueAt
--continue ) continueAt="$2"; continue=1; shift 2 ;;
# Compression level
--level ) level="$2"; shift 2 ;;
# Keep author number n
--keep-author ) keepArtist="$2"; shift 2 ;;
# Author override
--author ) authorOverride="$2"; shift 2 ;;
# Command synopsis.
-h | --help ) printf "$usage" $0 ; exit ;;
# Standard flag signifying the end of command line processing.
-- ) shift; break ;;
# Anything else stops command line processing.
* ) break ;;
esac
done
# -----
# Empty argv means we have nothing to do so lets bark some help.
if [ "$#" -eq 0 ]; then
printf "$usage" $0
exit 1
fi
# Setup safer bash script defaults.
set -o errexit -o noclobber -o nounset -o pipefail
codec=libmp3lame
extension=mp3
mode=chaptered
authcode=".authcode";
if [ -z ${HOME+x} ] && ! [ -z ${USERPROFILE+x} ]; then HOME="$USERPROFILE"; fi
authcodeDirs="${HOME}/ ./"
GREP=$(grep --version | grep -q GNU && echo "grep" || echo "ggrep")
if ! [[ $(type -P "$GREP") ]]; then
echo "$GREP (GNU grep) is not in your PATH"
echo "Without it, this script will break."
echo "On macOS, you may want to try: brew install grep"
exit 1
fi
if [ "$#" -eq 0 ]; then
echo "Usage: bash AXtoMP3 [--flac] [--aac] [--opus ] [--single] AUTHCODE {FILES}"
echo " Note that order does matter!"
exit 1
fi
if [[ "$1" = '--flac' ]]
then
codec=flac
extension=flac
shift
fi
if [[ "$1" == '--aac' ]]
then
codec=copy
extension=m4a
mode=single
shift
fi
if [[ "$1" = '--opus' ]]
then
codec=libopus
extension=ogg
shift
fi
if [[ "$1" == '--single' ]]
then
mode=single
shift
fi
auth_code="";
for dir in $authcodeDirs; do
codeFile="${dir}$authcode";
if [ ! -f "$codeFile" ]; then
auth_code=$1
shift
break;
elif [ -s "$codeFile" ]; then
auth_code=`head -1 "$codeFile"`
break;
else
echo "INFO: Sorry, empty \"$codeFile\" found, skipping.";
fi;
done;
if [ -z "$auth_code" ]; then
echo "INFO: Sorry, no authcode provided.";
exit 1;
fi;
# ========================================================================
# Utility Functions
# -----
# debug
# debug "Some longish message"
debug() {
echo "$(date "+%F %T%z") ${1}"
if [ $loglevel == 3 ] ; then
echo "$(date "+%F %T%z") DEBUG ${1}"
fi
}
# -----
# debug dump contents of a file to STDOUT
# debug "<full path to file>"
debug_file() {
if [ $loglevel == 3 ] ; then
echo "$(date "+%F %T%z") DEBUG"
echo "=Start=========================================================================="
cat "${1}"
echo "=End============================================================================"
fi
}
# -----
# debug dump a list of internal script variables to STDOUT
# debug_vars "Some Message" var1 var2 var3 var4 var5
debug_vars() {
if [ $loglevel == 3 ] ; then
msg="$1"; shift ; # Grab the message
args=("$@") # Grab the rest of the args
# determine the length of the longest key
l=0
for (( n=0; n<${#args[@]}; n++ )) ; do
(( "${#args[$n]}" > "$l" )) && l=${#args[$n]}
done
# Print the Debug Message
echo "$(date "+%F %T%z") DEBUG ${msg}"
echo "=Start=========================================================================="
# Using the max length of a var name we dynamically create the format.
fmt="%-"${l}"s = %s\n"
for (( n=0; n<${#args[@]}; n++ )) ; do
eval val="\$${args[$n]}" ; # We save off the value of the var in question for ease of coding.
echo "$(printf "${fmt}" ${args[$n]} "${val}" )"
done
echo "=End============================================================================"
fi
}
# -----
# log
log() {
if [ "$((${loglevel} > 1))" == "1" ] ; then
echo "$(date "+%F %T%z") ${1}"
else
echo "${1}"
fi
}
# -----
#progressbar produces a progressbar in the style of
# process: |####### | XX% (part/total unit)
# which is gonna be overwritten by the next line.
progressbar() {
#get input
part=${1}
total=${2}
#compute percentage and make print_percentage the same length regardless of the number of digits.
percentage=$((part*100/total))
if [ "$((percentage<10))" = "1" ]; then print_percentage=" $percentage"
elif [ "$((percentage<100))" = "1" ]; then print_percentage=" $percentage"
else print_percentage="$percentage"; fi
#draw progressbar with one # for every 5% and blank spaces for the missing part.
progressbar=""
for (( n=0; n<(percentage/5); n++ )) ; do progressbar="$progressbar#"; done
for (( n=0; n<(20-(percentage/5)); n++ )) ; do progressbar="$progressbar "; done
#print progressbar
echo -ne "Chapter splitting: |$progressbar| $print_percentage% ($part/$total chapters)\r"
}
# Print out what we have already after command line processing.
debug_vars "Command line options as set" codec extension mode container targetdir completedir auth_code keepArtist authorOverride
# ========================================================================
# Variable validation
# -----
# Detect which annoying version of grep we have
GREP=$(grep --version | grep -q GNU && echo "grep" || echo "ggrep")
if ! [[ $(type -P "$GREP") ]]; then
echo "$GREP (GNU grep) is not in your PATH"
echo "Without it, this script will break."
echo "On macOS, you may want to try: brew install grep"
exit 1
fi
# -----
# Detect which annoying version of sed we have
SED=$(sed --version 2>&1 | $GREP -q GNU && echo "sed" || echo "gsed")
if ! [[ $(type -P "$SED") ]]; then
echo "$SED (GNU sed) is not in your PATH"
echo "Without it, this script will break."
echo "On macOS, you may want to try: brew install gnu-sed"
exit 1
fi
# -----
# Detect ffmpeg and ffprobe
if [[ "x$(type -P ffmpeg)" == "x" ]]; then
echo "ERROR ffmpeg was not found on your env PATH variable"
echo "Without it, this script will break."
echo "INSTALL:"
echo "MacOS: brew install ffmpeg"
echo "Ubuntu: sudo apt-get update; sudo apt-get install ffmpeg libav-tools x264 x265 bc"
echo "Ubuntu (20.04): sudo apt-get update; sudo apt-get install ffmpeg x264 x265 bc"
echo "RHEL: yum install ffmpeg"
exit 1
fi
# -----
# Detect ffmpeg and ffprobe
if [[ "x$(type -P ffprobe)" == "x" ]]; then
echo "ERROR ffprobe was not found on your env PATH variable"
echo "Without it, this script will break."
echo "INSTALL:"
echo "MacOS: brew install ffmpeg"
echo "Ubuntu: sudo apt-get update; sudo apt-get install ffmpeg libav-tools x264 x265 bc"
echo "RHEL: yum install ffmpeg"
exit 1
fi
# -----
# Detect if we need mp4art for cover additions to m4a & m4b files.
if [[ "x${container}" == "xmp4" && "x$(type -P mp4art)" == "x" ]]; then
echo "WARN mp4art was not found on your env PATH variable"
echo "Without it, this script will not be able to add cover art to"
echo "m4b files. Note if there are no other errors the AAXtoMP3 will"
echo "continue. However no cover art will be added to the output."
echo "INSTALL:"
echo "MacOS: brew install mp4v2"
echo "Ubuntu: sudo apt-get install mp4v2-utils"
fi
# -----
# Detect if we need mp4chaps for adding chapters to m4a & m4b files.
if [[ "x${container}" == "xmp4" && "x$(type -P mp4chaps)" == "x" ]]; then
echo "WARN mp4chaps was not found on your env PATH variable"
echo "Without it, this script will not be able to add chapters to"
echo "m4a/b files. Note if there are no other errors the AAXtoMP3 will"
echo "continue. However no chapter data will be added to the output."
echo "INSTALL:"
echo "MacOS: brew install mp4v2"
echo "Ubuntu: sudo apt-get install mp4v2-utils"
fi
# -----
# Detect if we need mediainfo for adding description and narrator
if [[ "x$(type -P mediainfo)" == "x" ]]; then
echo "WARN mediainfo was not found on your env PATH variable"
echo "Without it, this script will not be able to add the narrator"
echo "and description tags. Note if there are no other errors the AAXtoMP3"
echo "will continue. However no such tags will be added to the output."
echo "INSTALL:"
echo "MacOS: brew install mediainfo"
echo "Ubuntu: sudo apt-get install mediainfo"
fi
# -----
# Obtain the authcode from either the command line, local directory or home directory.
# See Readme.md for details on how to acquire your personal authcode for your personal
# audible AAX files.
if [ -z $auth_code ]; then
if [ -r .authcode ]; then
auth_code=`head -1 .authcode`
elif [ -r ~/.authcode ]; then
auth_code=`head -1 ~/.authcode`
fi
fi
# No point going on if no authcode found.
if [ -z $auth_code ]; then
echo "ERROR Missing authcode"
echo "$usage"
exit 1
fi
# -----
# Check the target dir for if set if it is writable
if [[ "x${targetdir}" != "x" ]]; then
if [[ ! -w "${targetdir}" || ! -d "${targetdir}" ]] ; then
echo "ERROR Target Directory does not exist or is not writable: \"$targetdir\""
echo "$usage"
exit 1
fi
fi
# -----
# Check the completed dir for if set if it is writable
if [[ "x${completedir}" != "x" ]]; then
if [[ ! -w "${completedir}" || ! -d "${completedir}" ]] ; then
echo "ERROR Complete Directory does not exist or is not writable: \"$completedir\""
echo "$usage"
exit 1
fi
fi
# -----
# Check whether the loglevel is valid
if [ "$((${loglevel} < 0 || ${loglevel} > 3 ))" = "1" ]; then
echo "ERROR loglevel has to be in the range from 0 to 3!"
echo " 0: Show progress only"
echo " 1: default"
echo " 2: a little more information, timestamps"
echo " 3: debug"
echo "$usage"
exit 1
fi
# -----
# If a compression level is given, check whether the given codec supports compression level specifiers and whether the level is valid.
if [ "${level}" != "-1" ]; then
if [ "${codec}" == "flac" ]; then
if [ "$((${level} < 0 || ${level} > 12 ))" = "1" ]; then
echo "ERROR Flac compression level has to be in the range from 0 to 12!"
echo "$usage"
exit 1
fi
elif [ "${codec}" == "libopus" ]; then
if [ "$((${level} < 0 || ${level} > 10 ))" = "1" ]; then
echo "ERROR Opus compression level has to be in the range from 0 to 10!"
echo "$usage"
exit 1
fi
elif [ "${codec}" == "libmp3lame" ]; then
if [ "$((${level} < 0 || ${level} > 9 ))" = "1" ]; then
echo "ERROR MP3 compression level has to be in the range from 0 to 9!"
echo "$usage"
exit 1
fi
else
echo "ERROR This codec doesnt support compression levels!"
echo "$usage"
exit 1
fi
fi
# -----
# Clean up if someone hits ^c or the script exits for any reason.
trap 'rm -r -f "${working_directory}"' EXIT
# -----
# Set up some basic working files ASAP. Note the trap will clean this up no matter what.
working_directory=`mktemp -d 2>/dev/null || mktemp -d -t 'mytmpdir'`
metadata_file="${working_directory}/metadata.txt"
# -----
# Validate the AAX and extract the metadata associated with the file.
validate_aax() {
local media_file
media_file="$1"
# Test for existence
if [[ ! -r "${media_file}" ]] ; then
log "ERROR File NOT Found: ${media_file}"
return 1
else
if [[ "${VALIDATE}" == "1" ]]; then
log "Test 1 SUCCESS: ${media_file}"
fi
fi
# Clear the errexit value we want to capture the output of the ffprobe below.
set +e errexit
# Take a look at the aax file and see if it is valid.
output="$(ffprobe -loglevel warning -activation_bytes ${auth_code} -i "${media_file}" 2>&1)"
# If invalid then say something.
if [[ $? != "0" ]] ; then
# No matter what lets bark that something is wrong.
log "ERROR: Invalid File: ${media_file}"
elif [[ "${VALIDATE}" == "1" ]]; then
# If the validate option is present then lets at least state what is valid.
log "Test 2 SUCCESS: ${media_file}"
fi
# This is a big test only performed when the --validate switch is passed.
if [[ "${VALIDATE}" == "1" ]]; then
output="$(ffmpeg -hide_banner -activation_bytes ${auth_code} -i "${media_file}" -vn -f null - 2>&1)"
if [[ $? != "0" ]] ; then
log "ERROR: Invalid File: ${media_file}"
else
log "Test 3 SUCCESS: ${media_file}"
fi
fi
# Dump the output of the ffprobe command.
debug "$output"
# Turn it back on. ffprobe is done.
set -e errexit
}
# -----
# Inspect the AAX and extract the metadata associated with the file.
save_metadata() {
local media_file
media_file="$1"
ffprobe -i "$media_file" 2> "$metadata_file"
local media_file
media_file="$1"
ffprobe -i "$media_file" 2> "$metadata_file"
if [[ $(type -P mediainfo) ]]; then
echo "Mediainfo data START" >> "$metadata_file"
# Mediainfo output is structured like ffprobe, so we append it to the metadata file and then parse it with get_metadata_value()
# mediainfo "$media_file" >> "$metadata_file"
# Or we only get the data we are intrested in:
# Description
echo "Track_More :" "$(mediainfo --Inform="General;%Track_More%" "$media_file")" >> "$metadata_file"
# Narrator
echo "nrt :" "$(mediainfo --Inform="General;%nrt%" "$media_file")" >> "$metadata_file"
# Publisher
echo "pub :" "$(mediainfo --Inform="General;%pub%" "$media_file")" >> "$metadata_file"
echo "Mediainfo data END" >> "$metadata_file"
fi
debug "Metadata file $metadata_file"
debug_file "$metadata_file"
}
# -----
# Reach into the meta data and extract a specific value.
# This is a long pipe of transforms.
# This finds the first occurrence of the key : value pair.
get_metadata_value() {
local key
key="$1"
normalize_whitespace "$($GREP --max-count=1 --only-matching "${key} *: .*" "$metadata_file" | cut -d : -f 2- | sed -e 's#/##g;s/ (Unabridged)//' | tr -s '[:blank:]' ' ')"
local key
key="$1"
# Find the key in the meta data file # Extract field value # Remove the following /'s "(Unabridged) <blanks> at start end and multiples.
echo "$($GREP --max-count=1 --only-matching "${key} *: .*" "$metadata_file" | cut -d : -f 2- | $SED -e 's#/##g;s/ (Unabridged)//;s/^[[:blank:]]\+//g;s/[[:blank:]]\+$//g' | $SED 's/[[:blank:]]\+/ /g')"
}
# -----
# specific variant of get_metadata_value bitrate is important for transcoding.
get_bitrate() {
get_metadata_value bitrate | $GREP --only-matching '[0-9]\+'
get_metadata_value bitrate | $GREP --only-matching '[0-9]\+'
}
normalize_whitespace() {
echo $*
}
for path
# ========================================================================
# Main Transcode Loop
for aax_file
do
debug "Decoding ${path} with auth code ${auth_code}..."
save_metadata "${path}"
genre=$(get_metadata_value genre)
artist=$(get_metadata_value artist)
title=$(get_metadata_value title | sed 's/'\:'/'-\ '/g' | xargs -0)
output_directory="$(dirname "${path}")/${genre}/${artist}/${title}"
mkdir -p "${output_directory}"
full_file_path="${output_directory}/${title}.${extension}"
# Validate the input aax file. Note this happens no matter what.
# It's just that if the validate option is set then we skip to next file.
# If however validate is not set and we proceed with the script any errors will
# case the script to stop.
validate_aax "${aax_file}"
if [[ ${VALIDATE} == "1" ]] ; then
# Don't bother doing anything else with this file.
continue
fi
</dev/null ffmpeg -loglevel error -stats -activation_bytes "${auth_code}" -i "${path}" -vn -codec:a "${codec}" -ab "$(get_bitrate)k" -map_metadata -1 -metadata title="${title}" -metadata artist="${artist}" -metadata album_artist="$(get_metadata_value album_artist)" -metadata album="$(get_metadata_value album)" -metadata date="$(get_metadata_value date)" -metadata track="1/1" -metadata genre="${genre}" -metadata copyright="$(get_metadata_value copyright)" "${full_file_path}"
# -----
# Make sure everything is a variable. Simplifying Command interpretation
save_metadata "${aax_file}"
genre=$(get_metadata_value genre)
if [ "x${authorOverride}" != "x" ]; then
#Manual Override
artist="${authorOverride}"
album_artist="${authorOverride}"
else
if [ "${keepArtist}" != "-1" ]; then
# Choose artist from the one that are present in the metadata. Comma separated list of names
# remove leading space; 'C. S. Lewis' -> 'C.S. Lewis'
artist="$(get_metadata_value artist | cut -d',' -f"$keepArtist" | $SED -E 's|^ ||g; s|\. +|\.|g; s|((\w+\.)+)|\1 |g')"
album_artist="$(get_metadata_value album_artist | cut -d',' -f"$keepArtist" | $SED -E 's|^ ||g; s|\. +|\.|g; s|((\w+\.)+)|\1 |g')"
else
# The default
artist=$(get_metadata_value artist)
album_artist="$(get_metadata_value album_artist)"
fi
fi
title=$(get_metadata_value title | $SED 's/'\:'/'-'/g' | $SED 's/- /-/g' | xargs -0)
title=${title:0:100}
bitrate="$(get_bitrate)k"
album="$(get_metadata_value album)"
album_date="$(get_metadata_value date)"
copyright="$(get_metadata_value copyright)"
debug "Created ${full_file_path}."
# Get more tags with mediainfo
if [[ $(type -P mediainfo) ]]; then
narrator="$(get_metadata_value nrt)"
description="$(get_metadata_value Track_More)"
publisher="$(get_metadata_value pub)"
else
narrator=""
description=""
publisher=""
fi
cover_path="${output_directory}/cover.jpg"
debug "Extracting cover into ${cover_path}..."
</dev/null ffmpeg -loglevel error -activation_bytes "${auth_code}" -i "${path}" -an -codec:v copy "${cover_path}"
# Define the output_directory
if [ "${customDNS}" == "1" ]; then
currentDirNameScheme="$(eval echo "${dirNameScheme}")"
else
# The Default
currentDirNameScheme="${genre}/${artist}/${title}"
fi
if [ "${mode}" == "chaptered" ]; then
chaptercount=$($GREP -Pc "Chapter.*start.*end" $metadata_file)
debug "Extracting ${chaptercount} chapter files from ${full_file_path}..."
# If we defined a target directory, use it. Otherwise use the location of the AAX file
if [ "x${targetdir}" != "x" ] ; then
output_directory="${targetdir}/${currentDirNameScheme}/"
else
output_directory="$(dirname "${aax_file}")/${currentDirNameScheme}/"
fi
chapternum=1
while read -r -u9 first _ _ start _ end
do
if [[ "${first}" = "Chapter" ]]
then
read -r -u9 _
read -r -u9 _ _ chapter
chapter_title="${title} - $(printf %0${#chaptercount}d $chapternum) ${chapter}"
chapter_file="${output_directory}/${chapter_title}.${extension}"
# Define the output_file
if [ "${customFNS}" == "1" ]; then
currentFileNameScheme="$(eval echo "${fileNameScheme}")"
else
# The Default
currentFileNameScheme="${title}"
fi
output_file="${output_directory}/${currentFileNameScheme}.${extension}"
# the ID3 tags must only be specified for *.mp3 files,
# the other container formats come with their own
# tagging mechanisms.
id3_version_param=""
if test "${extension}" = "mp3"; then
id3_version_param="-id3v2_version 3"
fi
if [[ "${noclobber}" = "1" ]] && [[ -d "${output_directory}" ]]; then
log "Noclobber enabled but directory '${output_directory}' exists. Exiting to avoid overwriting"
exit 0
fi
mkdir -p "${output_directory}"
</dev/null ffmpeg -loglevel error -stats -i "${full_file_path}" -i "${cover_path}" -ss "${start%?}" -to "${end}" -map 0:0 -map 1:0 -acodec copy ${id3_version_param} \
-metadata:s:v title="Album cover" -metadata:s:v comment="Cover (Front)" -metadata track="${chapternum}" -metadata title="${chapter_title}" \
"${chapter_file}"
chapternum=$((chapternum + 1 ))
fi
done 9< "$metadata_file"
rm "${full_file_path}"
debug "Done creating chapters. Chaptered files contained in ${output_directory}."
if [ "$((${loglevel} > 0))" = "1" ]; then
# Fancy declaration of which book we are decoding. Including the AUTHCODE.
dashline="----------------------------------------------------"
log "$(printf '\n----Decoding---%s%s--%s--' "${title}" "${dashline:${#title}}" "${auth_code}")"
log "Source: ${aax_file}"
fi
# Big long DEBUG output. Fully describes the settings used for transcoding.
# Note this is a long debug command. It's not critical to operation. It's purely for people debugging
# and coders wanting to extend the script.
debug_vars "Book and Variable values" title auth_code mode aax_file container codec bitrate artist album_artist album album_date genre copyright narrator description publisher currentDirNameScheme output_directory currentFileNameScheme output_file metadata_file working_directory
# Display the total length of the audiobook in format hh:mm:ss
# 10#$var force base-10 interpretation. By default it's base-8, so values like 08 or 09 are not octal numbers
total_length="$(ffprobe -v error -activation_bytes "${auth_code}" -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 ${aax_file} | cut -d . -f 1)"
hours="$((total_length/3600))"
if [ "$((hours<10))" = "1" ]; then hours="0$hours"; fi
minutes="$((total_length/60-60*10#$hours))"
if [ "$((minutes<10))" = "1" ]; then minutes="0$minutes"; fi
seconds="$((total_length-3600*10#$hours-60*10#$minutes))"
if [ "$((seconds<10))" = "1" ]; then seconds="0$seconds"; fi
log "Total length: $hours:$minutes:$seconds"
# If level != -1 specify a compression level in ffmpeg.
compression_level_param=""
if [ "${level}" != "-1" ]; then
compression_level_param="-compression_level ${level}"
fi
# -----
if [ "${continue}" == "0" ]; then
# This is the main work horse command. This is the primary transcoder.
# This is the primary transcode. All the heavy lifting is here.
debug 'ffmpeg -loglevel error -stats -activation_bytes "${auth_code}" -i "${aax_file}" -vn -codec:a "${codec}" -ab ${bitrate} -map_metadata -1 -metadata title="${title}" -metadata artist="${artist}" -metadata album_artist="${album_artist}" -metadata album="${album}" -metadata date="${album_date}" -metadata track="1/1" -metadata genre="${genre}" -metadata copyright="${copyright}" "${output_file}"'
</dev/null ffmpeg -loglevel error \
-stats \
-activation_bytes "${auth_code}" \
-i "${aax_file}" \
-vn \
-codec:a "${codec}" \
${compression_level_param} \
-ab ${bitrate} \
-map_metadata -1 \
-metadata title="${title}" \
-metadata artist="${artist}" \
-metadata album_artist="${album_artist}" \
-metadata album="${album}" \
-metadata date="${album_date}" \
-metadata track="1/1" \
-metadata genre="${genre}" \
-metadata copyright="${copyright}" \
-metadata description="${description}" \
-metadata composer="${narrator}" \
-metadata publisher="${publisher}" \
-f ${container} \
"${output_file}"
if [ "$((${loglevel} > 0))" == "1" ]; then
log "Created ${output_file}."
fi
# -----
fi
# Grab the cover art if available.
cover_file="${output_directory}/cover.jpg"
if [ "${continue}" == "0" ]; then
if [ "$((${loglevel} > 1))" == "1" ]; then
log "Extracting cover into ${cover_file}..."
fi
</dev/null ffmpeg -loglevel error -activation_bytes "${auth_code}" -i "${aax_file}" -an -codec:v copy "${cover_file}"
fi
# -----
# OK now spit the file if that's what you want.
# If we want multiple file we take the big mp3 and split it by chapter.
# Not all audio encodings make sense with multiple chapter outputs. See options section
# for more detail
if [ "${mode}" == "chaptered" ]; then
# Playlist m3u support
playlist_file="${output_directory}/${currentFileNameScheme}.m3u"
if [ "${continue}" == "0" ]; then
if [ "$((${loglevel} > 0))" == "1" ]; then
log "Creating PlayList ${currentFileNameScheme}.m3u"
fi
echo '#EXTM3U' > "${playlist_file}"
fi
debug "Done."
rm "${metadata_file}"
# Determine the number of chapters.
chaptercount=$($GREP -Pc "Chapter.*start.*end" $metadata_file)
if [ "$((${loglevel} > 0))" == "1" ]; then
log "Extracting ${chaptercount} chapter files from ${output_file}..."
if [ "${continue}" == "1" ]; then
log "Continuing at chapter ${continueAt}:"
fi
fi
chapternum=1
#start progressbar for loglevel 0 and 1
if [ "$((${loglevel} < 2))" == "1" ]; then
progressbar 0 ${chaptercount}
fi
# We pipe the metadata_file in read.
# Example of the section that we are interested in:
#
# Chapter #0:0: start 0.000000, end 1928.231474
# Metadata:
# title : Chapter 1
#
# Then read the line in these variables:
# first Chapter
# _ #0:0:
# _ start
# chapter_start 0.000000,
# _ end
# chapter_end 1928.231474
while read -r -u9 first _ _ chapter_start _ chapter_end
do
# Do things only if the line starts with 'Chapter'
if [[ "${first}" = "Chapter" ]]; then
# The next line (Metadata:...) gets discarded
read -r -u9 _
# From the line 'title : Chapter 1' we save the third field and those after in chapter
read -r -u9 _ _ chapter
# The formatting of the chapters names and the file names.
# Chapter names are used in a few place.
# Define the chapter_file
if [ "${customCNS}" == "1" ]; then
chapter_title="$(eval echo "${chapterNameScheme}")"
else
# The Default
chapter_title="${title}-$(printf %0${#chaptercount}d $chapternum) ${chapter}"
fi
chapter_file="${output_directory}/${chapter_title}.${extension}"
# Since the .aax file allready got converted we can use
# -acodec copy, which is much faster than a reencodation.
# Since there is an issue when using copy on flac, where
# the duration of the chapters gets shown as if they where
# as long as the whole audiobook.
chapter_codec=""
if test "${extension}" = "flac"; then
chapter_codec="flac "${compression_level_param}""
else
chapter_codec="copy"
fi
#Since there seems to be a bug in some older versions of ffmpeg, which makes, that -ss and -to
#have to be apllied to the output file, this makes, that -ss and -to get applied on the input for
#ffmpeg version 4+ and on the output for all older versions.
split_input=""
split_output=""
if [ "$(($(ffmpeg -version | head -1 | cut -d \ -f 3 | cut -d . -f 1) > 3))" = "1" ]; then
split_input="-ss ${chapter_start%?} -to ${chapter_end}"
else
split_output="-ss ${chapter_start%?} -to ${chapter_end}"
fi
# Big Long chapter debug
debug_vars "Chapter Variables:" cover_file chapter_start chapter_end chapternum chapterNameScheme chapter_title chapter_file
if [ "$((${continueAt} > ${chapternum}))" = "0" ]; then
# Extract chapter by time stamps start and finish of chapter.
# This extracts based on time stamps start and end.
if [ "$((${loglevel} > 1))" == "1" ]; then
log "Splitting chapter ${chapternum}/${chaptercount} start:${chapter_start%?}(s) end:${chapter_end}(s)"
fi
</dev/null ffmpeg -loglevel quiet \
-nostats \
${split_input} \
-i "${output_file}" \
-i "${cover_file}" \
${split_output} \
-map 0:0 \
-map 1:0 \
-acodec ${chapter_codec} \
-metadata:s:v title="Album cover" \
-metadata:s:v comment="Cover (Front)" \
-metadata track="${chapternum}" \
-metadata title="${chapter_title}" \
-metadata:s:a title="${chapter_title}" \
-metadata:s:a track="${chapternum}" \
-map_chapters -1 \
-f ${container} \
"${chapter_file}"
# -----
if [ "$((${loglevel} < 2))" == "1" ]; then
progressbar ${chapternum} ${chaptercount}
fi
# OK lets get what need for the next chapter in the Playlist m3u file.
# Playlist creation.
duration=$(echo "${chapter_end} - ${chapter_start%?}" | bc)
echo "#EXTINF:${duration%.*},${title} - ${chapter}" >> "${playlist_file}"
echo "${chapter_title}.${extension}" >> "${playlist_file}"
fi
chapternum=$((chapternum + 1 ))
fi
done 9< "$metadata_file"
# Clean up of working directory stuff.
rm "${output_file}"
if [ "$((${loglevel} > 1))" == "1" ]; then
log "Done creating chapters for ${output_directory}."
else
#ending progress bar
echo ""
fi
else
# Perform file tasks on output file.
# ----
# ffmpeg seems to copy only chapter position, not chapter names.
if [[ ${container} == "mp4" && $(type -P mp4chaps) ]]; then
ffprobe -i "${aax_file}" -print_format csv -show_chapters 2>/dev/null | awk -F "," '{printf "CHAPTER%02d=%02d:%02d:%02.3f\nCHAPTER%02dNAME=%s\n", NR, $5/60/60, $5/60%60, $5%60, NR, $8}' > "${output_directory}/${fileNameScheme}.chapters.txt"
mp4chaps -i "${output_file}"
fi
fi
# -----
# Announce that we have completed the transcode
if [ "$((${loglevel} > 0))" == "1" ]; then
log "Complete ${title}"
fi
# Lastly get rid of any extra stuff.
rm "${metadata_file}"
# Move the aax file if the decode is completed and the --complete_dir is set to a valid location.
# Check the target dir for if set if it is writable
if [[ "x${completedir}" != "x" ]]; then
if [ "$((${loglevel} > 0))" == "1" ]; then
log "Moving Transcoded ${aax_file} to ${completedir}"
fi
mv "${aax_file}" "${completedir}"
fi
done

214
README.md
View File

@ -1,5 +1,5 @@
# AAXtoMP3
The purpose of this software is to convert AAX files to a more common MP3 format
The purpose of this software is to convert AAX files to common MP3, M4A, M4B, flac and ogg formats
through a basic bash script frontend to FFMPEG.
Audible uses this file format to maintain DRM restrictions on their audio
@ -12,15 +12,14 @@ your **personal** Audible account. The purpose of this software is to
create a method for you to download and store your books just in case
Audible fails for some reason.
## Setup
You will need your four byte authentication code that comes from Audible's
servers. This will be used by ffmpeg to perform the initial audio convert. You
can obtain this string from a tool like [audible-activator](https://github.com/inAudible-NG/audible-activator).
## Requirements
* bash 4.3.42 or later tested
* ffmpeg version 2.8.3 or later
* libmp3lame (came from lame package on Arch, not sure where else this is stored)
* grep Some OS distributions do not have it installed.
* sed Some OS versions will need to install gnu sed.
* mp4art used to add cover art to m4a and m4b files. Optional
* mediainfo used to add additional media tags like narrator. Optional
## OSX
Thanks to thibaudcolas, this script has been tested on OSX 10.11.6 El Capitan. YMMV, but it should work for
@ -29,19 +28,206 @@ conversions in OSX. It is recommended that you install GNU grep using 'brew inst
## AUR
Thanks to kbabioch, this script has also been packaged in the [AUR](https://aur.archlinux.org/packages/aaxtomp3-git/). Note that you will still need to extract your activation bytes before use.
## Usage
```bash
bash AAXtoMP3 [--flac] [--aac] [--opus] [--single] AUTHCODE {FILES}
## Usage(s)
```
bash AAXtoMP3 [-f|--flac] [-o|--opus] [-a|-aac] [-s|--single] [--level <COMPRESSIONLEVEL>] [-c|--chaptered] [-e:mp3] [-e:m4a] [-e:m4b] [-A|--authcode <AUTHCODE>] [-n|--no-clobber] [-t|--target_dir <PATH>] [-C|--complete_dir <PATH>] [-V|--validate] [-d|--debug] [-h|--help] [--continue <CHAPTERNUMBER>] <AAX INPUT_FILES>...
```
* AUTHCODE: **your** Audible auth code (it won't correctly decode otherwise) (required)
* Everything else is considered an input file, useful for batching!
Note that any optional flags have to be inputed *in order* to function correctly.
* **&lt;AAX INPUT_FILES&gt;**... are considered input file(s), useful for batching!
## Options
* **-f** or **--flac** Flac Encoding and as default produces a single file.
* **-o** or **--opus** Ogg/Opus Encoding defaults to multiple file output by chapter. The extension is .ogg
* **-a** or **--aac** AAC Encoding and produce a m4a single files output.
* **-A** or **--authcode &lt;AUTHCODE&gt;** for this execution of the command use the provided &lt;AUTHCODE&gt; to decode the AAX file.
* **-n** or **--no-clobber** If set and the target directory already exists, AAXtoMP3 will exit without overwriting anything.
* **-t** or **--target_dir &lt;PATH&gt;** change the default output location to the named &lt;PATH&gt;. Note the default location is ./Audiobook of the directory to which each AAX file resides.
* **-C** or **--complete_dir &lt;PATH&gt;** a directory to place aax files after they have been decoded successfully. Note make a back up of your aax files prior to using this option. Just in case something goes wrong.
* **-V** or **--validate** Perform 2 validation tests on the supplied aax files. This is more extensive than the normal validation as we attempt to transcode the aax file to a null file. This can take a long period of time. However it is useful when inspecting a large set of aax files prior to transcoding. As download errors are common with Audible servers.
* **-e:mp3** Identical to defaults.
* **-e:m4a** Create a m4a audio file. This is identical to --aac
* **-e:m4b** Create a m4b audio file. This is the book version of the m4a format.
* **-s** or **--single** Output a single file for the entire book. If you only want a single ogg file for instance.
* **-c** or **--chaptered** Output a single file per chapter. The `--chaptered` will only work if it follows the `--aac -e:m4a -e:m4b --flac` options.
* **--continue &lt;CHAPTERNUMBER&gt;** If the splitting into chapters gets interrupted (e.g. by a weak battery on your laptop) you can go on where the process got interrupted. Just delete the last chapter (which was incompletely generated) and redo the task with "--continue &lt;CHAPTERNUMBER&gt;" where CHAPTERNUMBER is the chapter that got interrupted.
* **--level &lt;COMPRESSIONLEVEL&gt;** Set compression level. May be given for mp3, flac and opus.
* **--keep-author &lt;FIELD&gt;** If a book has multiple authors and you don't want all of them in the metadata, with this flag you can specify a specific author (1 is the first, 2 is the second...) to keep while discarding the others.
* **--author &lt;AUTHOR&gt;** Manually set the author metadata field, useful if you have multiple books of the same author but the name reported is different (eg. spacing, accents..). Has precedence over `--keep-author`.
* **-l** or **--loglevel &lt;LOGLEVEL&gt;** Set loglevel: 0 = progress only, 1 (default) = more information, output of chapter splitting progress is limitted to a progressbar, 2 = more information, especially on chapter splitting, 3 = debug mode
* **--dir-naming-scheme &lt;STRING&gt;** or **-D** Use a custom directory naming scheme, with variables. See [below](#custom-naming-scheme) for more info.
* **--file-naming-scheme &lt;STRING&gt;** or **-F** Use a custom file naming scheme, with variables. See [below](#custom-naming-scheme) for more info.
* **--chapter-naming-scheme &lt;STRING&gt;** Use a custom chapter naming scheme, with variables. See [below](#custom-naming-scheme) for more info.
### [AUTHCODE]
**Your** Audible auth code (it won't correctly decode otherwise) (required).
#### Determining your own AUTHCODE
You will need your authentication code that comes from Audible's servers. This
will be used by ffmpeg to perform the initial audio convert. You can obtain
this string from a tool like
[audible-activator](https://github.com/inAudible-NG/audible-activator).
#### Specifying the AUTHCODE.
In order of __precidence__.
1. __--authcode [AUTHCODE]__ The command line option. With the highest precedence.
2. __.authcode__ If this file is placed in the current working directory and contains only the authcode it is used if the above is not.
3. __~/.authcode__ a global config file for all the tools. And is used as the default if none of the above are specified.
__Note:__ At least one of the above must be exist. The code must also match the encoding for the user that owns the AAX file(s). If the authcode does not match the AAX file no transcoding will occur.
### MP3 Encoding
* This is the **default** encoding
* Produces 1 or more mp3 files for the AAX title.
* The default mode is **chaptered**
* If you want a mp3 file per chapter do not use the **--single** option.
* A m3u playlist file will also be created in this instance in the case of **default** chaptered output.
* **--level** has to be in range 0-9, where 9 is fastest and 0 is highest quality. Please note: The quality can **never** become higher than the qualitiy of the original aax file!
### Ogg/Opus Encoding
* Can be done by using the **-o** or **--opus** command line switches
* The default mode is **chaptered**
* Opus coded files are stored in the ogg container format for better compatibility.
* **--level** has to be in range 0-10, where 0 is fastest and 10 is highest quality. Please note: The quality can **never** become higher than the qualitiy of the original aax file!
### AAC Encoding
* Can be done by using the **-a** or **--aac** command line switches
* The default mode is **single**
* Designed to be the successor of the MP3 format
* Generally achieves better sound quality than MP3 at the same bit rate.
* This will only produce 1 audio file as output.
### FLAC Encoding
* Can be done by using the **-f** or **--flac** command line switches
* The default mode is **single**
* FLAC is an open format with royalty-free licensing
* This will only produce 1 audio file as output. If you want a flac file per chapter do use **-c** or **--chaptered**.
* **--level** has to be in range 0-12, where 0 is fastest and 12 is highest compression. Since flac is lossless, the quality always remains the same.
### M4A and M4B Containers
* These containers were created by Apple Inc. They were meant to be the successor to mp3.
* M4A is a container that is meant to hold music and is typically of a higher bitrate.
* M4B is a container that is meant to hold audiobooks and is typically has bitrates of 64k and 32k.
* Both formats are chaptered
* Both support coverart internal
* The default mode is **single**
### Validating AAX files
* The **--validate** option will result in only a validation pass over the supplied aax file(s). No transcoding will occur. This is useful when you wish to ensure you have a proper download of your personal Audible audio books. With this option all supplied books are validated.
* If you do NOT supply the **--validate** option all audio books are still validated when they are processed. However if there is an invalid audio book in the supplied list of books the processing will stop at that point.
* A third test is performed on the file where the entire file is inspected to see if it is valid. This is a lengthy process. However it will not break the script when an invalid file is found.
* The 3 test current are:
1. aax present
1. meta data header in file is valid and complete
1. entire file is valid and complete. _only executed with the **--validate** option._
### Defaults
* Default out put directory is the base directory of each file listed. Plus the genre, Artist and Title of the Audio Book.
* The default codec is mp3
* The default output is by chapter.
### Custom naming scheme
The following flags can modify the default naming scheme:
* **--dir-naming-scheme** or **-D**
* **--file-naming-scheme** or **-F**
* **--chapter-naming-scheme**
Each flag takes a string as argument. If the string contains a variable defined in the script (eg. artist, title, chapter, narrator...), the corresponding value is used.
The default options correspond to the following flags:
* `--dir-naming-scheme '$genre/$artist/$title'`
* `--file-naming-scheme '$title'`
* `--chapter-naming-scheme '$title-$(printf %0${#chaptercount}d $chapternum) $chapter'`
Additional notes:
* If a command substitution is present in the passed string, (for example `$(printf %0${#chaptercount}d $chapternum)`, used to pad with zeros the chapter number), the commands are executed.
So you can use `--dir-naming-scheme '$(date +%Y)/$artist'`, but using `--file-naming-scheme '$(rm -rf /)'` is a really bad idea. Be careful.
* You can use basic text, like `--dir-naming-scheme 'Converted/$title'`
* You can also use shell variables as long as you escape them properly: `CustomGenre=Horror ./AAXtoMP3 --dir-naming-scheme "$CustomGenre/\$artist/\$title" *.aax`
* If you want shorter chapter names, use `--chapter-naming-scheme '$(printf %0${#chaptercount}d $chapternum) $chapter'`: only chapter number and chapter name
* If you want to append the narrator name to the title, use `--dir-naming-scheme '$genre/$artist/$title-$narrator' --file-naming-scheme '$title-$narrator'`
* If you don't want to have the books separated by author, use `--dir-naming-scheme '$genre/$title'`
### Installing Dependencies.
#### FFMPEG,FFPROBE
__Ubuntu, Linux Mint, Debian__
```
sudo apt-get update
sudo apt-get install ffmpeg libav-tools x264 x265 bc
```
__Fedora__
Fedora users need to enable the rpm fusion repository to install ffmpeg. Version 22 and upwards are currently supported. The following command works independent of your current version:
```
sudo dnf install https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm https://download1.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-$(rpm -E %fedora).noarch.rpm
```
Afterwards use the package manager to install ffmpeg:
```
sudo dnf install ffmpeg
```
__RHEL or compatible like CentOS__
RHEL version 6 and 7 are currently able to use rpm fusion.
In order to use rpm fusion you have to enable EPEL, see http://fedoraproject.org/wiki/EPEL
Add the rpm fusion repositories in version 6
```
sudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-6.noarch.rpm https://download1.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-6.noarch.rpm
```
or version 7:
```
sudo yum localinstall --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-7.noarch.rpm https://download1.rpmfusion.org/nonfree/el/rpmfusion-nonfree-release-7.noarch.rpm
```
then install ffmpeg:
```
sudo yum install ffmpeg
```
__MacOS__
```
brew install ffmpeg
brew install gnu-sed
brew install grep
```
#### mp4art/mp4chaps
_Note: This is an optional dependency._
__Ubuntu, Linux Mint, Debian__
```
sudo apt-get update
sudo apt-get install mp4v2-utils
```
__CentOS, RHEL & Fedora__
```
# CentOS/RHEL and Fedora users make sure that you have enabled atrpms repository in system. Lets begin installing FFmpeg as per your operating system.
yum install mp4v2-utils
```
__MacOS__
```
brew install mp4v2
```
#### mediainfo
_Note: This is an optional dependency._
__Ubuntu, Linux Mint, Debian__
```
sudo apt-get update
sudo apt-get install mediainfo
```
__CentOS, RHEL & Fedora__
```
yum install mediainfo
```
__MacOS__
```
brew install mediainfo
```
Additionally, if you have a .authcode file available in either your home directory or the current working directory, it will read the first line of that line and treat it like your auth_code. When you do this you do not need to specify an AUTHCODE input.
## Anti-Piracy Notice
Note that this project does NOT crack the DRM. It simply allows the user to
Note that this project **does NOT crack** the DRM. It simply allows the user to
use their own encryption key (fetched from Audible servers) to decrypt the
audiobook in the same manner that the official audiobook playing software does.