mirror of
https://github.com/KrumpetPirate/AAXtoMP3.git
synced 2025-04-19 10:28:36 +02:00
Compare commits
56 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
e66c51dbfa | ||
|
f6ec8aa867 | ||
|
f9bc150775 | ||
|
61cfc08886 | ||
|
1338f1e5c1 | ||
|
2a5a208a2d | ||
|
8fc5e360d3 | ||
|
1544a89249 | ||
|
c289c10cd7 | ||
|
1ac6979885 | ||
|
f65fddebc2 | ||
|
cab6394c02 | ||
|
3e42539812 | ||
|
c103d4899b | ||
|
be8473ffba | ||
|
6d659ade72 | ||
|
b07737f5c4 | ||
|
9a1f0fae49 | ||
|
bf7d405d3e | ||
|
1ce836f5a2 | ||
|
72794b6785 | ||
|
c9bcf2326b | ||
|
bee00b9c58 | ||
|
00fd4d552a | ||
|
aa702cb97a | ||
|
31507139c8 | ||
|
2272d9ce99 | ||
|
e4f92461d3 | ||
|
4dd095da2a | ||
|
20289a1774 | ||
|
048e15a476 | ||
|
d5e32478c7 | ||
|
5010927559 | ||
|
b87b08ee6a | ||
|
7f1f3df020 | ||
|
fb56087a72 | ||
|
c8870e29fd | ||
|
45fd0d4852 | ||
|
12435d3e24 | ||
|
ab91343dd5 | ||
|
c6e8a06f10 | ||
|
a45c6245fe | ||
|
9b24aa1be3 | ||
|
43d96f0118 | ||
|
ce62e832ee | ||
|
d645e616fe | ||
|
bf1f384341 | ||
|
f1c4b97bc1 | ||
|
68a91bbbbb | ||
|
58ecfbc72b | ||
|
0bd127ad06 | ||
|
fb86d4291c | ||
|
b549f57228 | ||
|
e76b5442c1 | ||
|
aab2045959 | ||
|
56ca165063 |
170
AAXtoMP3
170
AAXtoMP3
@ -9,14 +9,14 @@ usage=$'\nUsage: AAXtoMP3 [--flac] [--aac] [--opus ] [--single] [--level <COMPRE
|
||||
[--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>]
|
||||
[--use-audible-cli-data] [--continue <CHAPTERNUMBER>] {FILES}\n'
|
||||
[--use-audible-cli-data] [--audible-cli-library-file <LIBRARY_PATH>] [--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
|
||||
dirNameScheme= # Custom directory naming scheme, default is $genre/$artist/$title
|
||||
customDNS=0
|
||||
fileNameScheme= # Custom file naming scheme, default is $title
|
||||
customFNS=0
|
||||
@ -34,6 +34,10 @@ authorOverride= # Override the author, ignoring the metadata
|
||||
audibleCli=0 # Default off, Use additional data gathered from mkb79/audible-cli
|
||||
aaxc_key= # Initialize variables, in case we need them in debug_vars
|
||||
aaxc_iv= # Initialize variables, in case we need them in debug_vars
|
||||
ffmpegPath= # Set a custom path, useful for using the updated version that supports aaxc
|
||||
ffmpegName=ffmpeg # Set a custom ffmpeg binary name, useful tailoring to local setup
|
||||
ffprobeName=ffprobe # Set a custom ffprobe binary name, useful tailoring to local setup
|
||||
library_file= # Libraryfile generated by mkb79/audible-cli
|
||||
|
||||
# -----
|
||||
# Code tip Do not have any script above this point that calls a function or a binary. If you do
|
||||
@ -44,8 +48,6 @@ 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.
|
||||
@ -55,7 +57,7 @@ while true; do
|
||||
# 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 ;;
|
||||
-e:m4a | -a | --aac ) 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.
|
||||
@ -82,12 +84,20 @@ while true; do
|
||||
--continue ) continueAt="$2"; continue=1; shift 2 ;;
|
||||
# Use additional data got with mkb79/audible-cli
|
||||
--use-audible-cli-data ) audibleCli=1; shift ;;
|
||||
# Path of the library-file, generated by mkb79/audible-cli (audible library export -o ./library.tsv)
|
||||
-L | --audible-cli-library-file ) library_file="$2"; 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 ;;
|
||||
# Ffmpeg path override
|
||||
--ffmpeg-path ) ffmpegPath="$2"; shift 2 ;;
|
||||
# Ffmpeg name override
|
||||
--ffmpeg-name ) ffmpegName="$2"; shift 2 ;;
|
||||
# Ffprobe name override
|
||||
--ffprobe-name ) ffprobeName="$2"; shift 2 ;;
|
||||
# Command synopsis.
|
||||
-h | --help ) printf "$usage" $0 ; exit ;;
|
||||
# Standard flag signifying the end of command line processing.
|
||||
@ -212,6 +222,16 @@ else
|
||||
SED="gsed"
|
||||
fi
|
||||
|
||||
# Use custom ffmpeg (and ffprobe) binary ( --ffmpeg-path flag)
|
||||
if [ -n "$ffmpegPath" ]; then
|
||||
FFMPEG="$ffmpegPath/${ffmpegName}"
|
||||
FFPROBE="$ffmpegPath/${ffprobeName}"
|
||||
else
|
||||
FFMPEG="${ffmpegName}"
|
||||
FFPROBE="${ffprobeName}"
|
||||
fi
|
||||
|
||||
debug_vars "ffmpeg/ffprobe paths" FFMPEG FFPROBE
|
||||
|
||||
# -----
|
||||
# Detect which annoying version of grep we have
|
||||
@ -242,7 +262,7 @@ fi
|
||||
|
||||
# -----
|
||||
# Detect ffmpeg and ffprobe
|
||||
if [[ "x$(type -P ffmpeg)" == "x" ]]; then
|
||||
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:"
|
||||
@ -255,7 +275,7 @@ fi
|
||||
|
||||
# -----
|
||||
# Detect ffmpeg and ffprobe
|
||||
if [[ "x$(type -P ffprobe)" == "x" ]]; then
|
||||
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:"
|
||||
@ -402,7 +422,7 @@ validate_aax() {
|
||||
set +e errexit
|
||||
|
||||
# Take a look at the aax file and see if it is valid. If the source file is aaxc, we give ffprobe additional flags
|
||||
output="$(ffprobe -loglevel warning ${decrypt_param} -i "${media_file}" 2>&1)"
|
||||
output="$("$FFPROBE" -loglevel warning ${decrypt_param} -i "${media_file}" 2>&1)"
|
||||
|
||||
# If invalid then say something.
|
||||
if [[ $? != "0" ]] ; then
|
||||
@ -415,7 +435,7 @@ validate_aax() {
|
||||
|
||||
# This is a big test only performed when the --validate switch is passed.
|
||||
if [[ "${VALIDATE}" == "1" ]]; then
|
||||
output="$(ffmpeg -hide_banner ${decrypt_param} -i "${media_file}" -vn -f null - 2>&1)"
|
||||
output="$("$FFMPEG" -hide_banner ${decrypt_param} -i "${media_file}" -vn -f null - 2>&1)"
|
||||
if [[ $? != "0" ]] ; then
|
||||
log "ERROR: Invalid File: ${media_file}"
|
||||
else
|
||||
@ -477,6 +497,15 @@ validate_extra_files() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Test for library file
|
||||
if [[ ! -r "${library_file}" ]] ; then
|
||||
library_file_exists=0
|
||||
debug "library file not found"
|
||||
else
|
||||
library_file_exists=1
|
||||
debug "library file found"
|
||||
fi
|
||||
|
||||
debug "All expected audible-cli related file are here"
|
||||
}
|
||||
|
||||
@ -485,7 +514,7 @@ validate_extra_files() {
|
||||
save_metadata() {
|
||||
local media_file
|
||||
media_file="$1"
|
||||
ffprobe -i "$media_file" 2> "$metadata_file"
|
||||
"$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()
|
||||
@ -507,9 +536,42 @@ save_metadata() {
|
||||
# we use some characters (#) as placeholder, add some new lines,
|
||||
# put a ',' after the start value, we calculate the end of each chapter
|
||||
# as start+length, and we convert (divide) the time stamps from ms to s.
|
||||
# Then we delete all ':' since they make a filename invalid.
|
||||
# Then we delete all ':' and '/' since they make a filename invalid.
|
||||
jq -r '.content_metadata.chapter_info.chapters[] | "Chapter # start: \(.start_offset_ms/1000), end: \((.start_offset_ms+.length_ms)/1000) \n#\n# Title: \(.title)"' "${extra_chapter_file}" \
|
||||
| tr -d ':' >> "$metadata_file"
|
||||
| $SED 's@[:/]@@g' >> "$metadata_file"
|
||||
# In case we want to use a single file m4b we need to extract the
|
||||
# chapter titles from the .json generated by audible–cli and store
|
||||
# them correctly formatted for mp4chaps in a chapter.txt
|
||||
if [ "${mode}" == "single" ]; then
|
||||
# Creating a temp file to store the chapter data collected in save_metadata, as the output
|
||||
# folder will only be defined after save_metadata has been executed.
|
||||
# This file is only required when using audible-cli data and executing in single mode to
|
||||
# get proper chapter titles in single file m4b output.
|
||||
tmp_chapter_file="${working_directory}/chapter.txt"
|
||||
jq -r \
|
||||
'def pad(n): tostring | if (n > length) then ((n - length) * "0") + . else . end;
|
||||
.content_metadata.chapter_info.chapters |
|
||||
reduce .[] as $c ([]; if $c.chapters? then .+[$c | del(.chapters)]+[$c.chapters] else .+[$c] end) | flatten |
|
||||
to_entries |
|
||||
.[] |
|
||||
"CHAPTER\((.key))=\((((((.value.start_offset_ms / (1000*60*60)) /24 | floor) *24 ) + ((.value.start_offset_ms / (1000*60*60)) %24 | floor)) | pad(2))):\(((.value.start_offset_ms / (1000*60)) %60 | floor | pad(2))):\(((.value.start_offset_ms / 1000) %60 | floor | pad(2))).\((.value.start_offset_ms % 1000 | pad(3)))
|
||||
CHAPTER\((.key))NAME=\(.value.title)"' "${extra_chapter_file}" > "${tmp_chapter_file}"
|
||||
fi
|
||||
|
||||
# get extra meta data from library.tsv
|
||||
if [[ "${library_file_exists}" == 1 ]]; then
|
||||
asin=$(jq -r '.content_metadata.content_reference.asin' "${extra_chapter_file}")
|
||||
if [[ ! -z "${asin}" ]]; then
|
||||
lib_entry=$($GREP "^${asin}" "${library_file}")
|
||||
if [[ ! -z "${lib_entry}" ]]; then
|
||||
series_title=$(echo "${lib_entry}" | awk -F '\t' '{print $6}')
|
||||
series_sequence=$(echo "${lib_entry}" | awk -F '\t' '{print $7}')
|
||||
$SED -i "/^ Metadata:/a\\
|
||||
series : ${series_title}\\
|
||||
series_sequence : ${series_sequence}" "${metadata_file}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
debug "Metadata file $metadata_file"
|
||||
debug_file "$metadata_file"
|
||||
@ -617,6 +679,8 @@ do
|
||||
album="$(get_metadata_value album)"
|
||||
album_date="$(get_metadata_value date)"
|
||||
copyright="$(get_metadata_value copyright)"
|
||||
series="$(get_metadata_value series)"
|
||||
series_sequence="$(get_metadata_value series_sequence)"
|
||||
|
||||
# Get more tags with mediainfo
|
||||
if [[ $(type -P mediainfo) ]]; then
|
||||
@ -639,9 +703,9 @@ do
|
||||
|
||||
# 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}/"
|
||||
output_directory="${targetdir}/${currentDirNameScheme}"
|
||||
else
|
||||
output_directory="$(dirname "${aax_file}")/${currentDirNameScheme}/"
|
||||
output_directory="$(dirname "${aax_file}")/${currentDirNameScheme}"
|
||||
fi
|
||||
|
||||
# Define the output_file
|
||||
@ -654,8 +718,9 @@ do
|
||||
output_file="${output_directory}/${currentFileNameScheme}.${extension}"
|
||||
|
||||
if [[ "${noclobber}" = "1" ]] && [[ -d "${output_directory}" ]]; then
|
||||
log "Noclobber enabled but directory '${output_directory}' exists. Exiting to avoid overwriting"
|
||||
exit 0
|
||||
log "Noclobber enabled but directory '${output_directory}' exists. Skipping to avoid overwriting"
|
||||
rm -f "${metadata_file}" "${tmp_chapter_file}"
|
||||
continue
|
||||
fi
|
||||
mkdir -p "${output_directory}"
|
||||
|
||||
@ -674,7 +739,7 @@ do
|
||||
|
||||
# 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 ${decrypt_param} -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "${aax_file}" | cut -d . -f 1)"
|
||||
total_length="$("$FFPROBE" -v error ${decrypt_param} -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))"
|
||||
@ -693,8 +758,8 @@ do
|
||||
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 ${decrypt_param} -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 \
|
||||
debug '"$FFMPEG" -loglevel error -stats ${decrypt_param} -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 \
|
||||
${decrypt_param} \
|
||||
-i "${aax_file}" \
|
||||
@ -714,6 +779,8 @@ do
|
||||
-metadata description="${description}" \
|
||||
-metadata composer="${narrator}" \
|
||||
-metadata publisher="${publisher}" \
|
||||
-metadata series="${series}" \
|
||||
-metadata series_sequence="${series_sequence}" \
|
||||
-f ${container} \
|
||||
"${output_file}"
|
||||
if [ "$((${loglevel} > 0))" == "1" ]; then
|
||||
@ -722,8 +789,7 @@ do
|
||||
# -----
|
||||
fi
|
||||
# Grab the cover art if available.
|
||||
cover_file="${output_directory}/cover.jpg"
|
||||
extra_crop_cover=''
|
||||
cover_file="${output_directory}/${currentFileNameScheme}.jpg"
|
||||
if [ "${continue}" == "0" ]; then
|
||||
if [ "${audibleCli}" == "1" ]; then
|
||||
# We have a better quality cover file, copy it.
|
||||
@ -731,21 +797,27 @@ do
|
||||
log "Copy cover file to ${cover_file}..."
|
||||
fi
|
||||
cp "${extra_cover_file}" "${cover_file}"
|
||||
|
||||
# We now set a variable, ${extra_crop_cover}, which contains an additional
|
||||
# ffmpeg flag. It crops the cover so the width and the height is divisible by two.
|
||||
# Since the standard (in the aax file) image resolution is 512, we set the flag
|
||||
# only if we use a custom cover art.
|
||||
extra_crop_cover='-vf crop=trunc(iw/2)*2:trunc(ih/2)*2'
|
||||
else
|
||||
# Audible-cli not used, extract the cover from the aax file
|
||||
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}"
|
||||
</dev/null "$FFMPEG" -loglevel error -activation_bytes "${auth_code}" -i "${aax_file}" -an -codec:v copy "${cover_file}"
|
||||
fi
|
||||
fi
|
||||
|
||||
extra_crop_cover=''
|
||||
cover_width=$(ffprobe -i "${cover_file}" 2>&1 | $GREP -Po "[0-9]+(?=x[0-9]+)" | tail -n 1)
|
||||
if (( ${cover_width} % 2 == 1 )); then
|
||||
if [ "$((${loglevel} > 1))" == "1" ]; then
|
||||
log "Cover ${cover_file} has odd width ${cover_width}, setting extra_crop_cover to make even."
|
||||
fi
|
||||
# We now set a variable, ${extra_crop_cover}, which contains an additional
|
||||
# ffmpeg flag. It crops the cover so the width and the height is divisible by two.
|
||||
# Set the flag only if we use a cover art with an odd width.
|
||||
extra_crop_cover='-vf crop=trunc(iw/2)*2:trunc(ih/2)*2'
|
||||
fi
|
||||
|
||||
# -----
|
||||
# If mode=chaptered, split the big converted file by chapter and remove it afterwards.
|
||||
# Not all audio encodings make sense with multiple chapter outputs (see options section)
|
||||
@ -823,7 +895,7 @@ do
|
||||
#ffmpeg version 4+ and on the output for all older versions.
|
||||
split_input=""
|
||||
split_output=""
|
||||
if [ "$(($(ffmpeg -version | $SED -E 's/[^0-9]*([0-9]).*/\1/g;1q') > 3))" = "1" ]; then
|
||||
if [ "$(($("$FFMPEG" -version | $SED -E 's/[^0-9]*([0-9]).*/\1/g;1q') > 3))" = "1" ]; then
|
||||
split_input="-ss ${chapter_start%?} -to ${chapter_end}"
|
||||
else
|
||||
split_output="-ss ${chapter_start%?} -to ${chapter_end}"
|
||||
@ -837,7 +909,7 @@ do
|
||||
if [ "$((${loglevel} > 1))" == "1" ]; then
|
||||
log "Splitting chapter ${chapternum}/${chaptercount} start:${chapter_start%?}(s) end:${chapter_end}(s)"
|
||||
fi
|
||||
</dev/null ffmpeg -loglevel quiet \
|
||||
</dev/null "$FFMPEG" -loglevel quiet \
|
||||
-nostats \
|
||||
${split_input} \
|
||||
-i "${output_file}" \
|
||||
@ -848,7 +920,7 @@ do
|
||||
-map 1:0 \
|
||||
-acodec ${chapter_codec} \
|
||||
-metadata:s:v title="Album cover" \
|
||||
-metadata:s:v comment="Cover (Front)" \
|
||||
-metadata:s:v comment="Cover (front)" \
|
||||
-metadata track="${chapternum}" \
|
||||
-metadata title="${chapter}" \
|
||||
-metadata:s:a title="${chapter}" \
|
||||
@ -882,12 +954,46 @@ do
|
||||
# Perform file tasks on output file.
|
||||
# ----
|
||||
# ffmpeg seems to copy only chapter position, not chapter names.
|
||||
# use already created chapter.txt from save_metadata() in case
|
||||
# audible-cli data is used else use ffprobe to extract from m4b
|
||||
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}/${currentFileNameScheme}.chapters.txt"
|
||||
if [ "${audibleCli}" == "1" ]; then
|
||||
mv "${tmp_chapter_file}" "${output_directory}/${currentFileNameScheme}.chapters.txt"
|
||||
else
|
||||
"$FFPROBE" -i "${aax_file}" -print_format csv -show_chapters 2>/dev/null | awk -F "," '{printf "CHAPTER%d=%02d:%02d:%02.3f\nCHAPTER%dNAME=%s\n", NR, $5/60/60, $5/60%60, $5%60, NR, $8}' > "${output_directory}/${currentFileNameScheme}.chapters.txt"
|
||||
fi
|
||||
$SED -i 's/\,000/\.000/' "${output_directory}/${currentFileNameScheme}.chapters.txt"
|
||||
mp4chaps -i "${output_file}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f "${cover_file}" ]; then
|
||||
log "Adding cover art"
|
||||
# FFMPEG does not support MPEG-4 containers fully #
|
||||
if [ "${container}" == "mp4" ] ; then
|
||||
mp4art --add "${cover_file}" "${output_file}"
|
||||
# FFMPEG for everything else #
|
||||
else
|
||||
# Create temporary output file name - ensure extention matches previous appropriate output file to keep ffmpeg happy
|
||||
cover_output_file="${output_file%.*}.cover.${output_file##*.}"
|
||||
# Copy audio stream from current output, and video stream from cover file, setting appropriate metadata
|
||||
</dev/null "$FFMPEG" -loglevel quiet \
|
||||
-nostats \
|
||||
-i "${output_file}" \
|
||||
-i "${cover_file}" \
|
||||
-map 0:a:0 \
|
||||
-map 1:v:0 \
|
||||
-acodec copy \
|
||||
-vcodec copy \
|
||||
-id3v2_version 3 \
|
||||
-metadata:s:v title="Album cover" \
|
||||
-metadata:s:v comment="Cover (front)" \
|
||||
"${cover_output_file}"
|
||||
# Replace original output file with version including cover
|
||||
mv "${cover_output_file}" "${output_file}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# -----
|
||||
# Announce that we have completed the transcode
|
||||
if [ "$((${loglevel} > 0))" == "1" ]; then
|
||||
|
31
README.md
31
README.md
@ -25,7 +25,7 @@ Audible fails for some reason.
|
||||
|
||||
## 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] [--use-audible-cli-data]] [-d|--debug] [-h|--help] [--continue <CHAPTERNUMBER>] <AAX/AAXC INPUT_FILES>...
|
||||
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] [--use-audible-cli-data]] [-d|--debug] [-h|--help] [--continue <CHAPTERNUMBER>] <AAX/AAXC INPUT_FILES>...
|
||||
```
|
||||
or if you want to get guided through the options
|
||||
```
|
||||
@ -57,6 +57,10 @@ bash interactiveAAXtoMP3 [-a|--advanced] [-h|--help]
|
||||
* **--file-naming-scheme <STRING>** or **-F** Use a custom file naming scheme, with variables. See [below](#custom-naming-scheme) for more info.
|
||||
* **--chapter-naming-scheme <STRING>** Use a custom chapter naming scheme, with variables. See [below](#custom-naming-scheme) for more info.
|
||||
* **--use-audible-cli-data** Use additional data got with mkb79/audible-cli. See [below](#audible-cli-integration) for more info. Needed for the files in the `aaxc` format.
|
||||
* **--audible-cli-library-file** or **-L** Path of the library-file, generated by mkb79/audible-cli (`audible library export -o ./library.tsv`). Only available if `--use-audible-cli-data` is set. This file is required to parse additional metadata such as `$series` or `$series_sequence`.
|
||||
* **--ffmpeg-path** Set the ffmpeg/ffprobe binaries folder. Both of them must be executable and in the same folder.
|
||||
* **--ffmpeg-name** Set a custom name for the ffmpeg binary. Must be executable and in path, or in custom path specified by --ffmpeg-path.
|
||||
* **--ffprobe-name** Set a custom name for the ffprobe binary. Must be executable and in path, or in custom path specified by --ffmpeg-path.
|
||||
|
||||
## Options for interactiveAAXtoMP3
|
||||
* **-a** or **--advanced** Get more options to choose. Not used right now.
|
||||
@ -150,6 +154,10 @@ So you can use `--dir-naming-scheme '$(date +%Y)/$artist'`, but using `--file-na
|
||||
* 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'`
|
||||
* To be able to use `$series` or `$series_sequence` in the schemes the following is required:
|
||||
* `--use-audible-cli-data` is set
|
||||
* you have pre-generated the library-file via `audible library export -o ./library.tsv`
|
||||
* you have set the path to the generated library-file via `--audible-cli-library-file ./library.tsv`
|
||||
|
||||
### Installing Dependencies.
|
||||
In general, take a look at [command-not-found.com](https://command-not-found.com/)
|
||||
@ -160,6 +168,20 @@ sudo apt-get update
|
||||
sudo apt-get install ffmpeg libav-tools x264 x265 bc
|
||||
```
|
||||
|
||||
In Debian-based system's repositories the ffmpeg version is often outdated. If you want
|
||||
to convert .aaxc files, you need at least ffmpeg 4.4. So if your installed version
|
||||
needs to be updated, you can either install a custom repository that has the newer version,
|
||||
compile ffmpeg from source or download pre-compiled binaries.
|
||||
You can then tell AAXtoMP3 to use the compiled binaries with the `--ffmpeg-path` flag.
|
||||
You need to specify the folder where the ffmpeg and ffprobe binaries are. Make sure
|
||||
they are both executable.
|
||||
|
||||
If you have snapd installed, you can also install a recent version of 4.4 from the edge channel:
|
||||
```
|
||||
snap install ffmpeg --edge
|
||||
```
|
||||
In this case you will need to confiure a custom path _and_ binary name for ffprobe, `--ffmpeg-path /snap/bin/ --ffprobe-name ffmpeg.ffprobe`.
|
||||
|
||||
__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:
|
||||
@ -198,13 +220,17 @@ brew install findutils
|
||||
```
|
||||
|
||||
#### mp4art/mp4chaps
|
||||
_Note: This is an optional dependency._
|
||||
_Note: This is an optional dependency, required for adding cover art to m4a and b4b files only._
|
||||
|
||||
__Ubuntu, Linux Mint, Debian__
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get install mp4v2-utils
|
||||
```
|
||||
|
||||
On Debian and Ubuntu the mp4v2-utils package has been deprecated and removed, as the upsteam project is no longer maintained.
|
||||
The package was removed in Debian Buster, and Ubuntu Focal [ 20.04 ].
|
||||
|
||||
__CentOS, RHEL & Fedora__
|
||||
```
|
||||
# CentOS/RHEL and Fedora users make sure that you have enabled atrpms repository in system. Let’s begin installing FFmpeg as per your operating system.
|
||||
@ -239,6 +265,7 @@ Since getting those keys is not simple, for now the method used to get them
|
||||
is handled by the package audible-cli, that stores
|
||||
them in a file when downloading the aaxc file. This means that in order to
|
||||
decrypt the aaxc files, they must be downloaded with audible-cli.
|
||||
Note that you need at least [ffmpeg 4.4](#ffmpegffprobe).
|
||||
|
||||
## Audible-cli integration
|
||||
Some information are not present in the AAX file. For example the chapters's
|
||||
|
Loading…
x
Reference in New Issue
Block a user