mirror of
https://github.com/KrumpetPirate/AAXtoMP3.git
synced 2024-11-18 03:08:57 +01:00
Version 1.0
This commit is contained in:
parent
ed281c6336
commit
325ea95ebb
106
AAXtoMP3.pl
Executable file
106
AAXtoMP3.pl
Executable file
@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env perl
|
||||
use strict;
|
||||
use warnings;
|
||||
use v5.22.0;
|
||||
use autodie;
|
||||
use Getopt::Long qw/GetOptions/;
|
||||
Getopt::Long::Configure qw/gnu_getopt/;
|
||||
use File::Basename qw/fileparse/;
|
||||
use IO::CaptureOutput qw/capture_exec/;
|
||||
|
||||
say "Starting $0...";
|
||||
|
||||
my $VERBOSE;
|
||||
my $AUTHCODE;
|
||||
my $INPUT_FILE;
|
||||
my $CHAPTER;
|
||||
GetOptions(
|
||||
'auth|a=s' => \$AUTHCODE,
|
||||
'input|i=s' => \$INPUT_FILE,
|
||||
'verbose|v' => \$VERBOSE,
|
||||
'chapter|c' => \$CHAPTER,
|
||||
) or die "Usage: $0 -verbose -chapter -auth <AUDIBLE AUTH CODE> -input <INPUT FILE PATH>";
|
||||
die "Usage: $0 -verbose -chapter -auth <AUDIBLE AUTH CODE> -input <INPUT AAX PATH>"
|
||||
unless ($INPUT_FILE && $AUTHCODE);
|
||||
die "$INPUT_FILE is not a valid path" unless (-f $INPUT_FILE);
|
||||
|
||||
my ($filename, $dir, $extension) = fileparse($INPUT_FILE, qr/\.[^.]*/);
|
||||
my $ret = system_execute("ffprobe $INPUT_FILE");
|
||||
|
||||
my @fflines = split /\n/, $ret;
|
||||
my $file_type = pull_from_ffprobe("major_brand", @fflines);
|
||||
my $file_title = pull_from_ffprobe("title", @fflines);
|
||||
my $file_author = pull_from_ffprobe("artist", @fflines);
|
||||
my @chapters = grep { m/^\s+Chapter/ } @fflines;
|
||||
die "Input file ffprobe does not appear to match an AAX file type metadata"
|
||||
unless $file_type =~ /aax/;
|
||||
|
||||
my $output;
|
||||
if ($file_title && $file_author) {
|
||||
$output = "$file_title by $file_author";
|
||||
} else {
|
||||
$output = $filename;
|
||||
}
|
||||
my $output_sh_safe = quotemeta $output;
|
||||
|
||||
if (@chapters && $CHAPTER) {
|
||||
say "Attempting to convert AAX to MP3 by chapter. This will increase the processing time...";
|
||||
system_execute("mkdir -p $dir$output_sh_safe");
|
||||
my $chapter_index = 1;
|
||||
foreach my $chapter (@chapters) {
|
||||
my ($start, $duration) = extract_ts($chapter);
|
||||
my $output_fn = quotemeta "$dir$output/$output (Chapter $chapter_index).mp3";
|
||||
say "Converting Chapter $chapter_index START: $start DURATION: $duration...";
|
||||
$ret = system_execute("ffmpeg -activation_bytes $AUTHCODE -i $INPUT_FILE -vn -c:a libmp3lame -ab 128k -ss $start -t $duration $output_fn");
|
||||
$chapter_index++;
|
||||
say $ret if $VERBOSE;
|
||||
}
|
||||
} else {
|
||||
say "Using Audible auth code to copy AAX audio directly to MP3 format...";
|
||||
$ret = system_execute("ffmpeg -activation_bytes $AUTHCODE -i $INPUT_FILE -vn -c:a libmp3lame -ab 128k $dir$output_sh_safe.mp3");
|
||||
say $ret if $VERBOSE;
|
||||
}
|
||||
|
||||
say "End of $0...";
|
||||
|
||||
sub system_execute {
|
||||
my ($stdout, $stderr, $success, $exit_code) = capture_exec(@_);
|
||||
say "stdout: $stdout\nstderr: $stderr\nsuccess: $success\nexit code: $exit_code" if ($VERBOSE);
|
||||
if ($success) {
|
||||
return $stdout if $stdout;
|
||||
return $stderr if $stderr;
|
||||
} else {
|
||||
my $command = join " ", @_;
|
||||
die "Command $command was not successful, debug.";
|
||||
}
|
||||
}
|
||||
|
||||
sub extract_ts {
|
||||
my $line = shift;
|
||||
$line =~ s/.*://;
|
||||
$line =~ s/^\s*(.*?)\s*$/$1/;
|
||||
my @parts = split /\,/, $line;
|
||||
my $start = $parts[0];
|
||||
my $end = $parts[1];
|
||||
$start =~ s/.* //;
|
||||
$end =~ s/.* //;
|
||||
my $duration = $end - $start;
|
||||
return (convert_seconds($start), convert_seconds($duration));
|
||||
}
|
||||
|
||||
sub convert_seconds {
|
||||
my $sec = shift;
|
||||
my $time = sprintf "%02d:%02d:%02d.%03d",
|
||||
(gmtime($sec))[2,1,0, 0 % 1000];
|
||||
}
|
||||
|
||||
sub pull_from_ffprobe {
|
||||
my $desired = shift;
|
||||
my @fflines = @_;
|
||||
return unless ($desired && @fflines);
|
||||
my ($line) = grep { m/^\s+$desired/ } @fflines;
|
||||
$line =~ s/^\s*(.*?)\s*$/$1/;
|
||||
$line =~ s/.*://;
|
||||
$line =~ s/^\s*(.*?)\s*$/$1/;
|
||||
return $line;
|
||||
}
|
37
AAXtoMP3.py
37
AAXtoMP3.py
@ -1,37 +0,0 @@
|
||||
#!/usr/bin/python3
|
||||
import sys
|
||||
import getopt
|
||||
import shutil
|
||||
|
||||
def usage():
|
||||
print('Usage: '+sys.argv[0]+' -a <AMAZON AUTH STRING> -f <AAX FILE>')
|
||||
|
||||
def main(argv):
|
||||
try:
|
||||
opts, args = getopt.getopt(sys.argv[1:], 'a:f:h', ['auth=', 'file=', 'help'])
|
||||
except getopt.GetoptError:
|
||||
usage()
|
||||
sys.exit(2)
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt in ('-h', '--help'):
|
||||
usage()
|
||||
sys.exit(2)
|
||||
elif opt in ('-a', '--auth'):
|
||||
auth_string = arg
|
||||
elif opt in ('-f', '--file'):
|
||||
input_file = arg
|
||||
else:
|
||||
usage()
|
||||
sys.exit(2)
|
||||
if not auth_string:
|
||||
usage()
|
||||
sys.exit(2)
|
||||
if not input_file:
|
||||
usage()
|
||||
sys.exit(2)
|
||||
print(auth_string)
|
||||
print(input_file)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
39
README.md
39
README.md
@ -1,5 +1,6 @@
|
||||
# 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 a more common MP3 format
|
||||
through a basic perl script frontend to FFMPEG.
|
||||
|
||||
Audible uses this file format to maintain DRM restrictions on their audio
|
||||
books and if you download your book through your library it will be
|
||||
@ -16,8 +17,42 @@ You will need your four byte authitication 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).
|
||||
|
||||
## Anti-Piracy Notice
|
||||
## Requirements
|
||||
* perl version 5.22.0 or later
|
||||
* ffmpeg version 2.8.3 or later
|
||||
|
||||
You will also require the following perl modules:
|
||||
* autodie
|
||||
* Getopt::Long
|
||||
* File::Basename
|
||||
* IO::CaptureOutput
|
||||
|
||||
I would suggest installing cpanminus either through CPAN or (preferably) through your
|
||||
distro's repositories. Take note that many perl modules can be packaged in your repos
|
||||
as well so take a look there before resorting to cpan/cpanminus.
|
||||
|
||||
## Usage
|
||||
```
|
||||
perl AAXtoMP3.pl -a AUTHCODE -i AAXFILE -v -c
|
||||
```
|
||||
* -a: **your** Audible auth code (it won't correctly decode otherwise) (required)
|
||||
* -i: the input AAX file to be converted (required)
|
||||
* -v: verbose (optional)
|
||||
* -c: Convert to chapters based on input file's metadata information
|
||||
|
||||
## Known issues
|
||||
Currently the chapters mode (activated by the -c flag) does not work correctly.
|
||||
Parsing the metadata works fine but when the timestamps are converted to
|
||||
HH:MM:SS.xxx format there is some sort of hickup. It could be that Audible has some
|
||||
delay in the file to prevent this, but starting from Chapter 2 of the book it will
|
||||
no longer sync. I don't know if I will ever fix this, so avoid using the -c flag for now
|
||||
to produce one large MP3 file for the audiobook.
|
||||
|
||||
Also this was tested on Linux with the above requirements. No effort will be made to
|
||||
port this work to any other operating system, though it may work fine. Want a Windows/
|
||||
OSX port? You'll have to fork the work.
|
||||
|
||||
## Anti-Piracy Notice
|
||||
Note that this project does NOT ‘crack’ the DRM. It simplys 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.
|
||||
|
Loading…
Reference in New Issue
Block a user