Слушаю с помощью программы Ambling BookPlayer. Автор этой программы залег на дно, и уже не принимает советов и просьб по усовершенствованию программы (а у меня есть пара идеек). Зато GooglePlay уверенно принимает деньги за эту программу, чем я и воспользовался.
Аудио книги почему то принято разбивать на огромное количество файлов. Даже если один файл - одна глава, все равно это как то надумано. Мне почему то не нравится такая файло-помоечная структура. По крайней мере хранить этот бардак неудобно.
Я написал на bash небольшую программку, которая объединяет эти файлы в один, а заодно создает XML файл для экспорта в bookreader.
Работает она из под наутилуса Ubuntu.
#!/bin/bash #---------------------------------------------------------------------- # Программа объеденения музыкальных файлов (книг) # Для установки скопировать в папку # ~/.local/share/nautilus/scripts #---------------------------------------------------------------------- # --- Версионность --- # Начальная версия #VERSION=0.1 # улучшение алгоритмов кодирования #VERSION=0.2 #VERSION=0.3 # Заполнение полей в соответствии с xml описанием для импорта в ambling book reader #VERSION=0.4 # Добавлено переименование обложки в соответствии с названием книги. #VERSION=0.41 # Баг. Обложка не конвертится. (fixed) # Добавить полоску времени, а не пульсар (fixed) #VERSION=0.42 # Добавлено управление битрейтом, генерацией Обложки, XML, результирующего файла (fixed) VERSION=0.43 #---------------------------------------------------------------------- # Переменные и константы ERROR=192 INPUT="concat:" BITRATE="96k|32k|64k|128k|192k|256k" MIN_BITRATE="96k|32k|64k|128k|192k|256k" MAX_BITRATE="96k|32k|64k|128k|192k|256k" CODEC="libvorbis" CURENT_DIR=$(pwd) IN_DURATION=0 CREATE_COVER=TRUE CREATE_XML=TRUE CREATE_ONEOGG=TRUE #---------------------------------------------------------------------- # xml ambling bookplayer Tags BOOK_NAME="" AUTHOR_NAME="" NARRATOR_NAME="" PUBLISHER="" COPYRIGHT="" IMAGE_FILE_NAME="" SERIES_NAME="" SERIES_SEQUENCE="" #---------------------------------------------------------------------- # audiofile Tags (атавизм, времени не было сделать ревизию) TITLE="" ARTIST="" ALBUM_ARTIST="" ALBUM="" GENRE="AudioBook" DATE="" TRACK="" PUBLISHER="" ENCODED_BY="" COPYRIGHT="" COMPOSER="" PERFORMER="" DISC="" ENCODER="ffmpeg" COVER="" check_installed() { PKG_OK=$(dpkg-query -W --showformat='${Status}\n' $1 |grep "install ok installed") if [ "" == "$PKG_OK" ]; then zenity --no-wrap --error --text="$1 package not found!\n Install it" exit $ERROR fi } # Выход по ошибке exit_on_error() { yad --title "Error" --text "$1" 200 200 exit 192 } # Возвращает в секундах время звучания звукового файла duration() { RET=$(ffmpeg -i "$1" 2>&1 | grep Duration | awk '{ print $2 }' | tr -d ',') S=$(echo ${RET:0:2}*60*60+${RET:3:2}*60+${RET:6:2} | bc) echo $S } check_nautilus_env() { #-------------------------------------------------------------------- # Сохраняем для отладки echo "Аргументы командной строки запуска скрипта в Nautius" >> tst.txt echo "$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS" >> tst.txt echo "" >> tst.txt if [ "$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS" == "" ]; then echo "Этот скрипт запускается из под наутилуса" exit_on_error fi } ####################################################################### # Main code # #---------------------------------------------------------------------- check_nautilus_env check_installed yad check_installed python-mutagen #---------------------------------------------------------------------- # Получаем имена тегов AUTHOR_NAME=$(mid3iconv --encoding=Windows-1251 --dry-run --debug "$1" | grep TPE1 | sed 's/TPE1=//') if [ "$AUTHOR_NAME" == "" ]; then AUTHOR_NAME="$1" fi BOOK_NAME=$(mid3iconv --encoding=Windows-1251 --dry-run --debug "$1" | grep TIT2 | sed 's/TIT2=//') if [ "$BOOK_NAME" == "" ]; then BOOK_NAME="$1" fi SERIES_NAME="$BOOK_NAME" TAGS=$(yad --geometry=400x200 --center\ --title="Enter Track Tags" \ --form --text="$CURENT_DIR" \ --field="Название Книги" \ --field="Автор Книги" \ --field="Название серии" \ --field="Номер книги в серии" \ --field="Читает" \ --field="Обложка:FL" \ --field="Конвертировать обложку:CHK" \ --field="Создавать XML:CHK" \ --field="Создавать ogg файл:CHK" \ --field="Средний битрейт:CB" \ --field="Минимальный битрейт:CB" \ --field="Максимальный битрейт:CB" \ --item-separator='|' \ "$BOOK_NAME" \ "$AUTHOR_NAME" \ "$SERIES_NAME" \ "$SERIES_SEQUENCE" \ "$NARRATOR_NAME" \ "$IMAGE_FILE_NAME" \ $CREATE_COVER \ $CREATE_XML \ $CREATE_ONEOGG \ $BITRATE \ $MIN_BITRATE \ $MAX_BITRATE \ --button="gtk-ok:0" --button="gtk-cancel:1") if [[ $? -eq 1 ]]; then # cancel button pressed exit 192 fi BOOK_NAME=$(echo $TAGS | awk 'BEGIN {FS="|" } { print $1 }') BOOK_NAME=$(echo "$BOOK_NAME" | tr '[ ]' '[_]') AUTHOR_NAME=$(echo $TAGS | awk 'BEGIN {FS="|" } { print $2 }') AUTHOR_NAME=$(echo "$AUTHOR_NAME" | tr '[ ]' '[_]') SERIES_NAME=$(echo $TAGS | awk 'BEGIN {FS="|" } { print $3 }') SERIES_NAME=$(echo "$SERIES_NAME" | tr '[ ]' '[_]') SERIES_SEQUENCE=$(echo $TAGS | awk 'BEGIN {FS="|" } { print $4 }') SERIES_SEQUENCE=$(echo "$SERIES_SEQUENCE" | tr '[ ]' '[_]') NARRATOR_NAME=$(echo $TAGS | awk 'BEGIN {FS="|" } { print $5 }') NARRATOR_NAME=$(echo "$NARRATOR_NAME" | tr '[ ]' '[_]') IMAGE_FILE_NAME=$(echo $TAGS | awk 'BEGIN {FS="|" } { print $6 }') IMAGE_FILE_NAME=$(basename $IMAGE_FILE_NAME) CREATE_COVER=$(echo $TAGS | awk 'BEGIN {FS="|" } { print $7 }') CREATE_XML=$(echo $TAGS | awk 'BEGIN {FS="|" } { print $8 }') CREATE_ONEOGG=$(echo $TAGS | awk 'BEGIN {FS="|" } { print $9 }') BITRATE=$(echo $TAGS | awk 'BEGIN {FS="|" } { print $10 }') MIN_BITRATE=$(echo $TAGS | awk 'BEGIN {FS="|" } { print $11 }') MAX_BITRATE=$(echo $TAGS | awk 'BEGIN {FS="|" } { print $12 }') BUTTON_OK=$(echo $TAGS | awk 'BEGIN {FS="|" } { print $13 }') BUTTON_CANSEL=$(echo $TAGS | awk 'BEGIN {FS="|" } { print $14 }') #---------------------------------------------------------------------- # Сохраняем для отладки echo "RAW значения формы----------------------------------" > tst.txt echo $TAGS >> tst.txt echo " Разобраные значения формы---------------------------------- BOOK_NAME: $BOOK_NAME AUTHOR_NAME: $AUTHOR_NAME SERIES_NAME: $SERIES_NAME SERIES_SEQUENCE: $SERIES_SEQUENCE NARRATOR_NAME: $NARRATOR_NAME IMAGE_FILE_NAME: $IMAGE_FILE_NAME " >> tst.txt for arg do NF=$(echo "$arg" | tr '[ ]' '[_]') INPUT=$INPUT"|$NF" if [ ! "$arg" == "$NF" ]; then if [ -e "$NF" ]; then exit_on_error "$NF: File exist already.\n\nExiting." fi mv "$arg" "$NF"; fi NF_DURATION=$(duration $NF) IN_DURATION=$(( $IN_DURATION+$NF_DURATION )) done INPUT=$(echo "$INPUT" | sed 's/:|/:/') FFMPEG_CMD="ffmpeg -i \"$INPUT\" \\ -maxrate $MAX_BITRATE -minrate $MIN_BITRATE -ab $BITRATE -acodec $CODEC \\ -metadata title=\"$BOOK_NAME\" \\ -metadata artist=\"$AUTHOR_NAME\" \\ -metadata genre=\"Audiobook\" \\ -metadata album_artist=\"$NARRATOR_NAME\" \\ -metadata album=\"$SERIES_NAME\" \\ -metadata date=\"$DATE\" \\ -metadata track=\"$SERIES_SEQUENCE\" \\ -metadata publisher=\"$PUBLISHER\" \\ -metadata encoded_by=\"$ENCODED_BY\" \\ -metadata copyright=\"$COPYRIGHT\" \\ -metadata composer=\"$COMPOSER\" \\ -metadata performer=\"$PERFORMER\" \\ -metadata disc=\"$DISC\" \\ -metadata encoder=\"$ENCODER\" \\ \"$AUTHOR_NAME-$BOOK_NAME.ogg\"" #---------------------------------------------------------------------- # Сохраняем для отладки echo "--------------------------------" echo "Время зввучания: $IN_DURATION сек." >> tst.txt #---------------------------------------------------------------------- # Для отладки удобно эти команды скинуть в отдельные командные файлы echo "#!/bin/bash" > ffmpeg_cmd.sh echo "" >> ffmpeg_cmd.sh echo "$FFMPEG_CMD" >> ffmpeg_cmd.sh $(chmod +x ffmpeg_cmd.sh) echo "#!/bin/bash" > cover_convert_cmd.sh echo "" >> cover_convert_cmd.sh echo "convert $IMAGE_FILE_NAME -resize 400x400 $AUTHOR_NAME-$BOOK_NAME.jpg">> cover_convert_cmd.sh $(chmod +x cover_convert_cmd.sh) #---------------------------------------------------------------------- # Сконвертируем обложку книги if [ $CREATE_COVER == "TRUE" ]; then RET=$(./cover_convert_cmd.sh) fi #---------------------------------------------------------------------- # Сгенерим xml описание книги if [ $CREATE_XML == "TRUE" ]; then echo "" > $AUTHOR_NAME-$BOOK_NAME.xml fi #---------------------------------------------------------------------- # Собственно, запуск конвертирования if [ $CREATE_ONEOGG == "TRUE" ]; then touch /tmp/$$ ( ( echo 1 while [ -f /tmp/$$ ] do sleep 10 # Вычисления процента сконвертированного OUT_DURATION=$(duration "$AUTHOR_NAME-$BOOK_NAME.ogg") PERCENTAGE=$(( OUT_DURATION*100/IN_DURATION)) echo $PERCENTAGE done echo 100 ) | yad --title "$CURENT_DIR" \ --progress \ --progress-text="$AUTHOR_NAME-$BOOK_NAME.ogg" \ --percent=0 --auto-close --no-buttons --width=300) & RET=$(./ffmpeg_cmd.sh) rm /tmp/$$ wait fi #---------------------------------------------------------------------- # Конец yad --no-wrap \ --title "Конвертирование завершено" \ --text "$CURENT_DIR/$AUTHOR_NAME-$BOOK_NAME.ogg" rm ./cover_convert_cmd.sh rm ./ffmpeg_cmd.sh rm ./tst.txt $BOOK_NAME $AUTHOR_NAME /sdcard/audiobooks/$AUTHOR_NAME-$BOOK_NAME.jpg $SERIES_NAME $SERIES_SEQUENCE $NARRATOR_NAME $BOOK_NAME /sdcard/audiobooks/$AUTHOR_NAME-$BOOK_NAME.ogg
Если захотите ей воспользоваться, то сохрание ее в ~/.local/share/nautilus/scripts/merge_mp3.sh. После чего дайт права на исполнение следующей командой.
chmod +x ~/.local/share/nautilus/scripts/merge_mp3.shКроме того необходимо установить пакеты yad и python-mutagen
Дизастер! Оказалось, что в Android плохая реализация проигрывания ogg файлов с переменным битрейтом. Плеер не может точно позиционировать на выбранное место. Выглядит это так: вы выставляете проигрывание с позиции 1 час 28 мин 33 сек. Плеер передвигает вперед минут на 20 и начинает воспроизведение. За это я господ их Гугле материл. Бороться с этим можно только установив при кодировании постоянный битрейт.
Для книги вполне достаточно битрейта 96 к.
Полезные ссылки:
1. Кодирование аудио с потерями. Что к чему?
2. mutagen