diff --git a/AAXtoMP3 b/AAXtoMP3 index e32f0c1..dbf6895 100755 --- a/AAXtoMP3 +++ b/AAXtoMP3 @@ -7,8 +7,8 @@ # Usage Synopsis. usage=$'\nUsage: AAXtoMP3 [--flac] [--aac] [--opus ] [--single] [--level ] [--chaptered] [-e:mp3] [-e:m4a] [-e:m4b] [--authcode ] [--no-clobber] - [--target_dir ] [--complete_dir ] [--validate] - [--use-audible-cli-data] [--continue ] {FILES}\n' + [--target_dir ] [--complete_dir ] [--validate] [--keep-author ] + [--author ] [--use-audible-cli-data] [--continue ] {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. @@ -18,10 +18,12 @@ targetdir= # Optional output location. Note default is basedir 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 -DEBUG=0 # Default off, If set extremely verbose output. +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 audibleCli=0 # Default off, Use additional data gathered from mkb79/audible-cli # ----- @@ -56,7 +58,9 @@ while true; do # Don't overwrite the target directory if it already exists -n | --no-clobber ) noclobber=1; shift ;; # Extremely verbose output. - -d | --debug ) DEBUG=1; shift ;; + -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 @@ -65,6 +69,10 @@ while true; do --use-audible-cli-data ) audibleCli=1; shift ;; # 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. @@ -92,7 +100,7 @@ set -o errexit -o noclobber -o nounset -o pipefail # debug # debug "Some longish message" debug() { - if [ $DEBUG == 1 ] ; then + if [ $loglevel == 3 ] ; then echo "$(date "+%F %T%z") DEBUG ${1}" fi } @@ -101,7 +109,7 @@ debug() { # debug dump contents of a file to STDOUT # debug "" debug_file() { - if [ $DEBUG == 1 ] ; then + if [ $loglevel == 3 ] ; then echo "$(date "+%F %T%z") DEBUG" echo "=Start==========================================================================" cat "${1}" @@ -113,7 +121,7 @@ debug_file() { # debug dump a list of internal script variables to STDOUT # debug_vars "Some Message" var1 var2 var3 var4 var5 debug_vars() { - if [ $DEBUG == 1 ] ; then + if [ $loglevel == 3 ] ; then msg="$1"; shift ; # Grab the message args=("$@") # Grab the rest of the args @@ -142,12 +150,39 @@ debug_vars() { # ----- # log log() { - echo "$(date "+%F %T%z") ${1}" + 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 audibleCli +debug_vars "Command line options as set" codec extension mode container targetdir completedir auth_code keepArtist authorOverride audibleCli # ======================================================================== # Variable validation @@ -272,6 +307,17 @@ if [[ "x${completedir}" != "x" ]]; then 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 @@ -473,7 +519,22 @@ do # Make sure everything is a variable. Simplifying Command interpretation save_metadata "${aax_file}" genre=$(get_metadata_value genre) - artist=$(get_metadata_value artist) + 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} if [ "x${targetdir}" != "x" ] ; then @@ -483,7 +544,6 @@ do fi output_file="${output_directory}/${title}.${extension}" bitrate="$(get_bitrate)k" - album_artist="$(get_metadata_value album_artist)" album="$(get_metadata_value album)" album_date="$(get_metadata_value date)" copyright="$(get_metadata_value copyright)" @@ -503,16 +563,28 @@ do fi mkdir -p "${output_directory}" - # 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}" + 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 output_file metadata_file working_directory + # Display the total length of the audiobook in format hh:mm:ss + 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*hours))" + if [ "$((minutes<10))" = "1" ]; then minutes="0$minutes"; fi + seconds="$((total_length-3600*hours-60*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 @@ -546,8 +618,9 @@ do -metadata publisher="${publisher}" \ -f ${container} \ "${output_file}" - - log "Created ${output_file}." + if [ "$((${loglevel} > 0))" == "1" ]; then + log "Created ${output_file}." + fi # ----- fi # Grab the cover art if available. @@ -556,7 +629,9 @@ do if [ "${continue}" == "0" ]; then if [ "${audibleCli}" == "1" ]; then # We have a better quality cover file, copy it. - log "Copy cover file to ${cover_file}..." + if [ "$((${loglevel} > 1))" == "1" ]; then + 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 @@ -565,7 +640,10 @@ do # only if we use a custom cover art. extra_crop_cover='-vf crop=trunc(iw/2)*2:trunc(ih/2)*2' else - log "Extracting cover into ${cover_file}..." + # Audible-cli not used, extract the cover from the aax file + if [ "$((${loglevel} > 1))" == "1" ]; then + log "Extracting cover into ${cover_file}..." + fi 0))" == "1" ]; then + log "Creating PlayList ${title}.m3u" + fi echo '#EXTM3U' > "${playlist_file}" fi # Determine the number of chapters. chaptercount=$($GREP -Pc "Chapter.*start.*end" $metadata_file) - log "Extracting ${chaptercount} chapter files from ${output_file}..." - if [ "${continue}" == "1" ]; then - log "Continuing at chapter ${continueAt}:" + 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: # @@ -617,20 +703,26 @@ do chapter_title="${title}-$(printf %0${#chaptercount}d $chapternum) ${chapter}" chapter_file="${output_directory}/${chapter_title}.${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" + # 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 # Big Long chapter debug - debug_vars "Chapter Variables:" cover_file chapter_start chapter_end id3_version_param chapternum chapter chapter_title chapter_file + debug_vars "Chapter Variables:" cover_file chapter_start chapter_end chapternum chapter 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. - log "Splitting chapter ${chapternum}/${chaptercount} start:${chapter_start%?}(s) end:${chapter_end}(s)" + if [ "$((${loglevel} > 1))" == "1" ]; then + log "Splitting chapter ${chapternum}/${chaptercount} start:${chapter_start%?}(s) end:${chapter_end}(s)" + fi 1))" == "1" ]; then + log "Added cover art to ${chapter_title}" + fi fi fi @@ -673,14 +768,21 @@ do # Clean up of working directory stuff. rm "${output_file}" - log "Done creating chapters for ${output_directory}." + 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. # ---- # Add the cover art to m4a and m4b file types. if [[ ${container} == "mp4" && $(type -P mp4art) ]]; then mp4art -q --add "${cover_file}" "${output_file}" - log "Added cover art to ${title}.${extension}" + if [ "$((${loglevel} > 1))" == "1" ]; then + log "Added cover art to ${title}.${extension}" + fi fi 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}/${title}.chapters.txt" @@ -690,14 +792,18 @@ do # ----- # Announce that we have completed the transcode - log "Complete ${title}" + 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 - log "Moving Transcoded ${aax_file} to ${completedir}" + if [ "$((${loglevel} > 0))" == "1" ]; then + log "Moving Transcoded ${aax_file} to ${completedir}" + fi mv "${aax_file}" "${completedir}" fi diff --git a/README.md b/README.md index 7e75183..1cb327b 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,9 @@ bash AAXtoMP3 [-f|--flac] [-o|--opus] [-a|-aac] [-s|--single] [--level