Giter VIP home page Giter VIP logo

m4b-tool's Introduction

m4b-tool

m4b-tool is a is a wrapper for ffmpeg and mp4v2 to merge, split or and manipulate audiobook files with chapters. Although m4b-tool is designed to handle m4b files, nearly all audio formats should be supported, e.g. mp3, aac, ogg, alac and flac.

Support me via GitHub sponsors

If you are using any of my projects and find them helpful, please consider donating to support me. I plan to use the money to support other open source projects or charitable purposes. Thank you!

Current monthly sponsors >= 25.00$

Special thanks to all sponsors donating a monthly amount of >= 25.00$.

Name Amount
numinit 25.00$

Become a sponsor

sponsor me and donate

Usage with Nix

Get Nix and ensure that Flakes are enabled.

  • Running: nix run github:sandreas/m4b-tool or nix run github:sandreas/m4b-tool#m4b-tool-libfdk
    • The latter will build FFMpeg using libfdk_aac, which will take longer.
  • Building: nix build github:sandreas/m4b-tool or nix build github:sandreas/m4b-tool#m4b-tool-libfdk
    • Wrapper script is located at ./result/bin/m4b-tool
  • Developing: Clone and nix develop
    • When done updating dependencies, run composer2nix --executable --composition=composer.nix to update the .nix files

Announcement

I started an experiment, that now has reached an early alpha level and can be tried out. The command line tool is written in C#, fully open source and is called tone. It already has a pretty decent feature set, so if you would like to try it, here it is:

https://github.com/sandreas/tone

This announcement does NOT mean, that m4b-tool is deprecated or will be soon. The development of m4b-tool will go on (at least until tone has a feature-set similar to m4b-tool). It's just to have an alternative tool for features, that may have limitations.

Have fun, can't wait to get your feedback.

❗❗❗Important Note❗❗❗

Unfortunately I am pretty busy at the moment, so m4b-tool 0.4.2 is very old. Since it is not planned to release a newer version without having complete documentation, there is only the latest pre-release getting bug fixes. It is already pretty stable, so if you are experiencing bugs with v0.4.2, please try the latest pre-release, if it has been already fixed there.

Thank you, sandreas

https://pilabor.com

Features

  • merge a set of audio files (e.g. MP3 or AAC) into a single m4b file
  • split a single m4b file into several output files by chapters or a flac encoded album into single tracks via cue sheet
  • Add or adjust chapters for an existing m4b file via silence detection or musicbrainz

TL;DR - examples for the most common tasks

Merge multiple files

merge all audio files in directory data/my-audio-book into file data/merged.m4b (tags are retained and data/my-audio-book/cover.jpg and data/my-audio-book/description.txt are embedded, if available)

m4b-tool merge "data/my-audio-book/" --output-file="data/merged.m4b"

Split one file by chapters

split one big m4b file by chapters into multiple mp3 files at data/my-audio-book_splitted/ (tags are retained, data/my-audio-book_splitted/cover.jpg is created, if m4b contains a cover)

m4b-tool split --audio-format mp3 --audio-bitrate 96k --audio-channels 1 --audio-samplerate 22050 "data/my-audio-book.m4b"

Chapters adjustment of a file via silence detection

chapters can try to adjust existing chapters of an m4b by silence detection

m4b-tool chapters --adjust-by-silence -o "data/destination-with-adjusted-chapters.m4b" "data/source-with-misplaced-chapters.m4b"

Best practices

Since the most used subcommand of m4b-tool seems to be merge, lets talk about best practice...

Step 0 - Take a look at the docker image

Unfortunately m4b-tool has many dependencies. Not only one-liners, if you would like to get the best quality and tagging support, many dependencies have to be compiled manually with extra options. Thats why you should take a look at the docker image, which comes with all the bells and whistles of top audio quality, top tagging support and easy installation and has almost no disadvantages.

Note: If you are on windows, it might be difficult to make it work

Step 1 - Organizing your audiobooks in directories

When merging audiobooks, you should prepare them - the following directory structure helps a lot, even if you only merge one single audiobook:

input/<main genre>/<author>/<title>

or if it is a series

input/<main genre>/<author>/<series>/<series-part> - <title>

Examples:

input/Fantasy/J.K. Rowling/Quidditch Through the Ages/
input/Fantasy/J.K. Rowling/Harry Potter/1 - Harry Potter and the Philosopher's Stone/

Note: If your audiobook title contains invalid path characters like /, just replace them with a dash -.

Step 2 - add cover and a description

Now, because you almost always want a cover and a description for your audiobook, you should add the following files in the main directory:

  • cover.jpg (or cover.jpeg or cover.png)
  • description.txt (Be sure to use UTF-8 text file encoding for the contents)

Examples:

input/Fantasy/J.K. Rowling/Quidditch Through the Ages/cover.jpg
input/Fantasy/J.K. Rowling/Quidditch Through the Ages/description.txt

Note: m4b-tool will find and embed these files automatically but does not fail, if they are not present

Step 3 - chapters

Chapters are nice to add waypoints for your audiobook. They help to remember the last position and improve the experience in general.

fixed chapters

If you would like to adjust chapters manually, you can add a chapters.txt (same location as cover.jpg) with following contents (<chapter-start> <chapter-title>):

00:00:00.000 Intro
00:04:19.153 This is
00:09:24.078 A way to add
00:14:34.500 Chapters manually

by tag

If your input files are tagged, these tags will be used to create the chapter metadata by its title. So if you tag your input files with valid chapter names as track title, this will result in a nice and clean m4b-file with valid chapter names.

by length

Another great feature since m4b-tool v.0.4.0 is the --max-chapter-length parameter. Often the individual input files are too big which results in chapters with a very long duration. This can be annoying, if you would like to jump to a certain point, since you have to rewind or fast-forward and hold the button for a long time, instead of just tipping previous or next a few times. To automatically add sub-chapters, you could provide:

--max-chapter-length=300,900

This will cause m4b-tool

  • Trying to preserve original chapters as long as they are not longer than 15 minutes (900 seconds)
  • If a track is longer than 15 minutes
    • Perform a silence detection and try to add sub-chapters at every silence between 5 minutes (300 seconds) and 15 minutes (900 seconds)
    • If no silence is detected, add a hard cut sub-chapter every 5 minutes

Sub-chapters are named like the original and get an additional index. This is a nice way to keep the real names but not having chapters with a too long duration.

Step 4 (optional) - for iPod owners

If you own an iPod, there might be a problem with too long audiobooks, since iPods only support 32bit sampling rates. If your audiobook is longer than 27 hours with 22050Hz sampling rate, you could provide --adjust-for-ipod, to automatically downsample your audiobook, which results in lower quality, but at least its working on your good old iPod...

Step 5 (optional) - more cpu cores, faster conversion

m4b-tool supports multiple conversion tasks in parallel with the --jobs parameter (e.g. --jobs=2). If you have to convert more than one file, which is the common case, you nearly double the merge speed by providing the --jobs=2 parameter (or quadruplicate with --jobs=4, if you have a quad core system, etc.). Don't provide a number higher than the number of cores on your system - this will slow down the merge...

Note: If you run the conversion on all your cores, it will result in almost 100% CPU usage, which may lead to slower system performance

Step 6 - Use the --batch-pattern feature

In m4b-tool v.0.4.0 the --batch-pattern feature was added. It can be used to batch-convert multiple audiobooks at once, but also to just convert one single audiobook - because you can create tags from an existing directory structure.

Hint: The output-file parameter has to be a directory, when using --batch-pattern.

Even multiple --batch-pattern parameters are supported, while the first match will be used first. So if you created the directory structure as described above, the final command to merge input/Fantasy/Harry Potter/1 - Harry Potter and the Philosopher's Stone/ to output/Fantasy/Harry Potter/1 - Harry Potter and the Philosopher's Stone.m4b would look like this:

m4b-tool merge -v --jobs=2 --output-file="output/" --max-chapter-length=300,900 --adjust-for-ipod --batch-pattern="input/%g/%a/%s/%p - %n/"  --batch-pattern="input/%g/%a/%n/" "input/"

In --batch-pattern mode, existing files are skipped by default

Result

If you performed the above steps with the docker image or installed and compiled all dependencies, you should get the following result:

  • Top quality audio by using libfdk_aac encoder
  • Series and single audiobooks have valid tags for genre, author, title, sorttitle, etc. from --batch-pattern usage
  • If the files cover.jpg (or cover.jpeg or cover.png) and description.txt exist in the main directories, a cover, a description and a longdesc are embedded
  • If you tagged the input files, real chapter names should appear in your player
  • No more chapters longer than 15 minutes
  • Working iPod versions for audiobooks longer than 27 hours

Installation

Docker

To use docker with m4b-tool, you first have to

  • pull the official docker image (recommended)
  • or build the Dockerfile in the main directory

Official image

The official docker images are available on DockerHub. They are somewhat experimental, but have proven to work well. The latest tag is considered as way to go with the bleeding edge features and fixes. Every now and then a dated tag is published (e.g. sandreas/m4b-tool:2022-09-25), that is considered as pretty stable, to ensure a broken latest image will not break your whole setup.

# pull the image
docker pull sandreas/m4b-tool:latest

# create an alias for m4b-tool running docker
alias m4b-tool='docker run -it --rm -u $(id -u):$(id -g) -v "$(pwd)":/mnt sandreas/m4b-tool:latest'

# testing the command
m4b-tool --version

Note: If you use the alias above, keep in mind that you cannot use absolute paths (e.g. /tmp/data/audiobooks/harry potter 1) or symlinks. You must change into the directory and use relative paths (e.g. cd /tmp/data && m4b-tool merge "audiobooks/harry potter 1" --output-file harry.m4b)

Build manually or use a specific release version

To manually build a docker container for a specific m4b-tool release, it is required to provide an extra parameter for downloading a specific version into the image, e.g. for v.0.4.1:

# clone m4b-tool repository
git clone https://github.com/sandreas/m4b-tool.git

# change directory
cd m4b-tool

# build docker image - this will take a while
docker build . -t m4b-tool

# create an alias for m4b-tool running docker
alias m4b-tool='docker run -it --rm -u $(id -u):$(id -g) -v "$(pwd)":/mnt m4b-tool'

# testing the command
m4b-tool --version

# use the specific pre-release from 2022-07-16
docker build . --build-arg M4B_TOOL_DOWNLOAD_LINK=https://github.com/sandreas/m4b-tool/files/9125095/m4b-tool.tar.gz -t m4b-tool

Note: You could also just edit the according variable in the Dockerfile.

Dockerize a custom build, that is not available via download link

Developers or experts might want to run a complete custom build of m4b-tool or build the code themselves (e.g. if you forked the repository and applied some patches). If that is the case, you can store the custom build to dist/m4b-tool.phar relative to the Dockerfile and then do a default build.

# dist/m4b-tool.phar is available
docker build . -t m4b-tool

After this the custom build should be integrated into the docker image.

MacOS

On MacOS you may use the awesome package manager brew to install m4b-tool.

Recommended: High audio quality, sort tagging

Getting best audio quality requires some additional effort. You have to recompile ffmpeg with the non-free libfdk_aac codec. This requires uninstalling the default ffmpeg package if installed, since brew dropped the possibility for extra options. There is no official ffmpeg-with-options repository, but a pretty decent tap, that you could use to save time.

# FIRST INSTALL ONLY: if not already done, remove existing ffmpeg with default audio quality options
# check for ffmpeg with libfdk and uninstall if libfdk is not already available
[ -x "$(which ffmpeg)" ] && (ffmpeg -hide_banner -codecs 2>&1 | grep libfdk || brew uninstall ffmpeg)

# tap required repositories
brew tap sandreas/tap
brew tap homebrew-ffmpeg/ffmpeg

# check available ffmpeg options and which you would like to use
brew options homebrew-ffmpeg/ffmpeg/ffmpeg

# install ffmpeg with at least libfdk_aac for best audio quality
brew install homebrew-ffmpeg/ffmpeg/ffmpeg --with-fdk-aac

# install m4b-tool
brew install sandreas/tap/m4b-tool

# check installed m4b-tool version
m4b-tool --version

Stick to defaults (acceptable audio quality, no sort tagging)

If the above did not work for you or you would just to checkout m4b-tool before using it in production, you might want to try the quick and easy way. It will work, but you get lower audio quality and there is no support for sort tagging.

# tap m4b-tool repository
brew tap sandreas/tap

# install dependencies
brew install ffmpeg fdk-aac-encoder mp4v2

# install m4b-tool with acceptable audio quality and no sort tagging
brew install --ignore-dependencies sandreas/tap/m4b-tool

Ubuntu

# install all dependencies
sudo apt install ffmpeg mp4v2-utils fdkaac php-cli php-intl php-json php-mbstring php-xml

# install / upgrade m4b-tool
sudo wget https://github.com/sandreas/m4b-tool/releases/download/v.0.4.2/m4b-tool.phar -O /usr/local/bin/m4b-tool && sudo chmod +x /usr/local/bin/m4b-tool

# check installed m4b-tool version
m4b-tool --version

Note: If you would like to get the best possible audio quality, you have to compile ffmpeg with the high quality encoder fdk-aac (--enable-libfdk_aac) - see https://trac.ffmpeg.org/wiki/CompilationGuide/Ubuntu for a step-by-step guide to compile ffmpeg.

Manual installation (only recommended on Windows systems)

m4b-tool is written in PHP and uses ffmpeg, mp4v2 and optionally fdkaac for high efficiency codecs to perform conversions. Therefore you will need the following tools in your %PATH%:

To check the dependencies, running following commands via command line should show similar output:

$ php -v
Copyright (c) 1997-2018 The PHP Group [...]

$ ffmpeg -version
ffmpeg version 4.1.1 Copyright (c) 2000-2019 the FFmpeg developers [...]

$ mp4chaps --version
mp4chaps - MP4v2 2.0.0

$ fdkaac
fdkaac 1.0.0 [...]

If you are sure, all dependencies are installed, the next step is to download the latest release of m4b-tool from

https://github.com/sandreas/m4b-tool/releases

Depending on the operating system, you can rename m4b-tool.phar to m4b-tool and run m4b-tool --version directly from the command line. If you are not sure, you can always use the command php m4b-tool.phar --version to check if the installation was successful. This should work on every system.

If you would like to use the latest source code with all new features and fixes, you could also build from source. The current build might be in unstable and should only be used for testing purposes or if you need a specific feature that has not been released.

Custom mp4v2 for accurate sorting order

Most audiobooks are not released in alphabetical order. A prominent example is Harry Potter. So if you have all the Harry Potter audiobooks, it depends on your player, but probably they are not listed in the correct order... let's see, what the alphabetical order would be:

  • Harry Potter and the Chamber of Secrets (Part 2)
  • Harry Potter and the Philosopher's Stone (Part 1)
  • Harry Potter and the Prisoner of Azkaban (Part 3)

And the correct order would have been:

  • Harry Potter and the Philosopher's Stone (Part 1)
  • Harry Potter and the Chamber of Secrets (Part 2)
  • Harry Potter and the Prisoner of Azkaban (Part 3)

Well, there is a solution for this. You have to tag the audiobook with a custom sortname and / or sortalbum. If your player supports these tags, the order is now correct, even when the title is still the original title. To achieve this, i had to build a custom version of mp4v2 (more accurate mp4tags), to add options for these tags and add the pseudo tags --series and --series-part.

So if you do the following:

m4b-tool merge --name="Harry Potter and the Chamber of Secrets" --series="Harry Potter" --series-part="2" --output-file="output/Harry Potter and the Chamber of Secrets.m4b" "input/Harry Potter and the Chamber of Secrets"

It would result in:

  • Name: Harry Potter and the Chamber of Secrets
  • Sortname: Harry Potter 2 - Harry Potter and the Chamber of Secrets

Install custom mp4v2

In the docker image, the custom version is already installed

git clone https://github.com/sandreas/mp4v2
cd mp4v2
./configure
make && sudo make install

About audio quality

In m4b-tool all audio conversions are performed with ffmpeg resulting in pretty descent audio quality using its free encoders. However, best quality takes some extra effort, so if you are using the free encoders, m4b-tool might show the following hint:

Your ffmpeg version cannot produce top quality aac using encoder aac instead of libfdk_aac

That's not really a problem, because the difference between the aac and libfdk_aac encoder is hardly noticeable in most cases. But to overcome the hint and get the best audio quality possible, you have to use a non-free encoder, that is not integrated in ffmpeg by default (licensing reasons). Depending on the operating system you are using, installing the non-free encoder may require a little extra skills, effort and time (see the notes for your operating system above). You have to decide, if it is worth the additional effort for getting the slightly better quality. If you are using the docker image, you should get the best quality by default.

If you are using very low bitrates (<= 32k), you could also use high efficiency profiles to further improve audio quality (e.g. --audio-profile=aac_he for mono). Unfortunately, ffmpeg's high efficiency implementation produces audio files, that are incompatible with many players (including iTunes). To produce high efficiency files, that are compatible with at least most common players, you will need to install fdkaac for now.

More Details:

Submitting issues

You think there is an issue with m4b-tool? First take a look at the Known Issues below. If this does not help, please provide the following information when adding an issue:

  • the operating system you use
  • the exact command, that you tried, e.g. m4b-tool merge my-audio-book/ --output-file merged.m4b
  • the error message, that occured or the circumstances, e.g. the resulting file merged.m4b is only 5kb
  • other relevant information, e.g. sample files if needed

Example:

Title: m4b-tool does not embed covers

If i run m4b-tool with a folder containing a cover.png, it does not embed the cover and shows an error message.

OS: Ubuntu 16.04 LTS
Command: `m4b-tool merge my-audio-book/ ---output-file merged.m4b`
Error: Cannot embed cover, cover is not a valid image file

Attached files: cover.png

Known issues

If you are getting PHP Exceptions, it is a configuration issue with PHP in most cases. If are not familiar with PHP configuration, you could follow these instructions, to fix a few known issues:

Exception Charset not supported

[Exception]
  charset windows-1252 is not supported - use one of these instead: utf-8

This mostly happens on windows, because the mbstring-Extension is used to internally convert charsets, so that special chars like german umlauts are supported on every platform. To fix this, you need to enable the mbstring-extension:

Run php --ini on the command line:

C:\>php --ini
...
Loaded Configuration File:         C:\Program Files\php\php.ini

Open the configuration file (e.g. C:\Program Files\php\php.ini) in a text editor and search for extension=. On Windows there should be an item like this:

;extension=php_mbstring.dll

remove the ; to enable the extension:

extension=php_mbstring.dll

Now everything should work as expected.

m4b-tool commands

The following list contains all possible commands including merge, split and chapters accompanied by the reference of parameters available in every command.

merge

With m4b-tool you can merge a set of audio files to one single m4b audiobook file.

Example:

m4b-tool merge "data/my-audio-book" --output-file="data/my-audio-book.m4b"

This merges all Audio-Files in folder data/my-audio-book into my-audio-book.m4b, using the tag-title of every file for generating chapters.

If there is a file data/my-audio-book/cover.jpg (or cover.jpeg or cover.png), it will be used as cover for the resulting m4b file.

Note: If you use untagged audio files, you could provide a musicbrainz id to get the correct chapter names, see command chapter for more info.

Reference

For all options, see m4b-tool merge --help:

Description:
  Merges a set of files to one single file

Usage:
  merge [options] [--] <input> [<more-input-files>...]

Arguments:
  input                                          Input file or folder
  more-input-files                               Other Input files or folders

Options:
      --logfile[=LOGFILE]                        file to log all output [default: ""]
      --debug                                    enable debug mode - sets verbosity to debug, logfile to m4b-tool.log and temporary encoded files are not deleted
  -f, --force                                    force overwrite of existing files
      --no-cache                                 clear cache completely before doing anything
      --ffmpeg-threads[=FFMPEG-THREADS]          specify -threads parameter for ffmpeg - you should also consider --jobs when merge is used [default: ""]
      --platform-charset[=PLATFORM-CHARSET]      Convert from this filesystem charset to utf-8, when tagging files (e.g. Windows-1252, mainly used on Windows Systems) [default: ""]
      --ffmpeg-param[=FFMPEG-PARAM]              Add argument to every ffmpeg call, append after all other ffmpeg parameters (e.g. --ffmpeg-param="-max_muxing_queue_size" --ffmpeg-param="1000" for ffmpeg [...] -max_muxing_queue_size 1000) (multiple values allowed)
  -a, --silence-min-length[=SILENCE-MIN-LENGTH]  silence minimum length in milliseconds [default: 1750]
  -b, --silence-max-length[=SILENCE-MAX-LENGTH]  silence maximum length in milliseconds [default: 0]
      --max-chapter-length[=MAX-CHAPTER-LENGTH]  maximum chapter length in seconds - its also possible to provide a desired chapter length in form of 300,900 where 300 is desired and 900 is max - if the max chapter length is exceeded, the chapter is placed on the first silence between desired and max chapter length [default: "0"]
      --name[=NAME]                              custom name, otherwise the existing metadata will be used
      --sortname[=SORTNAME]                      custom sortname, that is used only for sorting
      --album[=ALBUM]                            custom album, otherwise the existing metadata for name will be used
      --sortalbum[=SORTALBUM]                    custom sortalbum, that is used only for sorting
      --artist[=ARTIST]                          custom artist, otherwise the existing metadata will be used
      --sortartist[=SORTARTIST]                  custom sortartist, that is used only for sorting
      --genre[=GENRE]                            custom genre, otherwise the existing metadata will be used
      --writer[=WRITER]                          custom writer, otherwise the existing metadata will be used
      --albumartist[=ALBUMARTIST]                custom albumartist, otherwise the existing metadata will be used
      --year[=YEAR]                              custom year, otherwise the existing metadata will be used
      --description[=DESCRIPTION]                custom short description, otherwise the existing metadata will be used
      --longdesc[=LONGDESC]                      custom long description, otherwise the existing metadata will be used
      --comment[=COMMENT]                        custom comment, otherwise the existing metadata will be used
      --copyright[=COPYRIGHT]                    custom copyright, otherwise the existing metadata will be used
      --encoded-by[=ENCODED-BY]                  custom encoded-by, otherwise the existing metadata will be used
      --cover[=COVER]                            custom cover, otherwise the existing metadata will be used
      --skip-cover                               skip extracting and embedding covers
      --series[=SERIES]                          custom series, this pseudo tag will be used to auto create sort order (e.g. Harry Potter or The Kingkiller Chronicles)
      --series-part[=SERIES-PART]                custom series part, this pseudo tag will be used to auto create sort order (e.g. 1 or 2.5)
      --audio-format[=AUDIO-FORMAT]              output format, that ffmpeg will use to create files [default: "m4b"]
      --audio-channels[=AUDIO-CHANNELS]          audio channels, e.g. 1, 2 [default: ""]
      --audio-bitrate[=AUDIO-BITRATE]            audio bitrate, e.g. 64k, 128k, ... [default: ""]
      --audio-samplerate[=AUDIO-SAMPLERATE]      audio samplerate, e.g. 22050, 44100, ... [default: ""]
      --audio-codec[=AUDIO-CODEC]                audio codec, e.g. libmp3lame, aac, ... [default: ""]
      --audio-profile[=AUDIO-PROFILE]            audio profile, when using extra low bitrate - valid values: aac_he, aac_he_v2 [default: ""]
      --adjust-for-ipod                          auto adjust bitrate and sampling rate for ipod, if track is too long (may result in low audio quality)
      --fix-mime-type                            try to fix MIME-type (e.g. from video/mp4 to audio/mp4) - this is needed for some players to prevent an empty video window
  -o, --output-file=OUTPUT-FILE                  output file
      --include-extensions[=INCLUDE-EXTENSIONS]  comma separated list of file extensions to include (others are skipped) [default: "aac,alac,flac,m4a,m4b,mp3,oga,ogg,wav,wma,mp4"]
  -m, --musicbrainz-id=MUSICBRAINZ-ID            musicbrainz id so load chapters from
      --no-conversion                            skip conversion (destination file uses same encoding as source - all encoding specific options will be ignored)
      --batch-pattern[=BATCH-PATTERN]            multiple batch patterns that can be used to merge all audio books in a directory matching the given patterns (e.g. %a/%t for author/title) - parameter --output-file must be a directory (multiple values allowed)
      --dry-run                                  perform a dry run without converting all the files in batch mode (requires --batch-pattern)
      --jobs[=JOBS]                              Specifies the number of jobs (commands) to run simultaneously [default: 1]
      --use-filenames-as-chapters                Use filenames for chapter titles instead of tag contents
      --no-chapter-reindexing                    Do not perform any reindexing for index-only chapter names (by default m4b-tool will try to detect index-only chapters like Chapter 1, Chapter 2 and reindex it with its numbers only)
  -h, --help                                     Display this help message
  -q, --quiet                                    Do not output any message
  -V, --version                                  Display this application version
      --ansi                                     Force ANSI output
      --no-ansi                                  Disable ANSI output
  -n, --no-interaction                           Do not ask any interactive question
  -v|vv|vvv, --verbose                           Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Placeholder reference for --batch-pattern

If you use the --batch-pattern parameter, the following placeholders are supported

  • title / name: %n
  • sort_name: %N
  • album: %m,
  • sort_album: %M,
  • artist: %a,
  • sort_artist: %A,
  • genre: %g,
  • writer: %w,
  • album_artist: %t,
  • year: %y,
  • description: %d,
  • long_description: %D,
  • comment: %c,
  • copyright: %C,
  • encoded_by: %e,
  • group(ing): %G,
  • purchase_date: %U,
  • series: %s,
  • series_part: %p,

split

m4b-tool can be used to split a single m4b into a file per chapter or a flac encoded album into single tracks via cue sheet.

Example:

m4b-tool split --audio-format mp3 --audio-bitrate 96k --audio-channels 1 --audio-samplerate 22050 "data/my-audio-book.m4b"

This splits the file data/my-audio-book.m4b into an mp3 file for each chapter, writing the files into data/my-audio-book_splitted/.

Cue sheet splitting (experimental)

If you would like to split a flac file containing multiple tracks, a cue sheet with the exact filename of the flac is required (my-album.flac requires my-album.cue):

# my-album.cue is automatically found and used for splitting
m4b-tool split --audio-format=mp3 --audio-bitrate=192k --audio-channels=2 --audio-samplerate=48000 "data/my-album.flac"

Reference

For all options, see m4b-tool split --help:

Description:
  Splits an m4b file into parts

Usage:
  split [options] [--] <input>

Arguments:
  input                                          Input file or folder

Options:
      --logfile[=LOGFILE]                        file to dump all output [default: ""]
      --debug                                    enable debug mode - sets verbosity to debug, logfile to m4b-tool.log and temporary files are not deleted
  -f, --force                                    force overwrite of existing files
      --no-cache                                 do not use cached values and clear cache completely
      --ffmpeg-threads[=FFMPEG-THREADS]          specify -threads parameter for ffmpeg [default: ""]
      --platform-charset[=PLATFORM-CHARSET]      Convert from this filesystem charset to utf-8, when tagging files (e.g. Windows-1252, mainly used on Windows Systems) [default: ""]
      --ffmpeg-param[=FFMPEG-PARAM]              Add argument to every ffmpeg call, append after all other ffmpeg parameters (e.g. --ffmpeg-param="-max_muxing_queue_size" --ffmpeg-param="1000" for ffmpeg [...] -max_muxing_queue_size 1000) (multiple values allowed)
  -a, --silence-min-length[=SILENCE-MIN-LENGTH]  silence minimum length in milliseconds [default: 1750]
  -b, --silence-max-length[=SILENCE-MAX-LENGTH]  silence maximum length in milliseconds [default: 0]
      --max-chapter-length[=MAX-CHAPTER-LENGTH]  maximum chapter length in seconds - its also possible to provide a desired chapter length in form of 300,900 where 300 is desired and 900 is max - if the max chapter length is exceeded, the chapter is placed on the first silence between desired and max chapter length [default: "0"]
      --audio-format[=AUDIO-FORMAT]              output format, that ffmpeg will use to create files [default: "m4b"]
      --audio-channels[=AUDIO-CHANNELS]          audio channels, e.g. 1, 2 [default: ""]
      --audio-bitrate[=AUDIO-BITRATE]            audio bitrate, e.g. 64k, 128k, ... [default: ""]
      --audio-samplerate[=AUDIO-SAMPLERATE]      audio samplerate, e.g. 22050, 44100, ... [default: ""]
      --audio-codec[=AUDIO-CODEC]                audio codec, e.g. libmp3lame, aac, ... [default: ""]
      --audio-profile[=AUDIO-PROFILE]            audio profile, when using extra low bitrate - valid values (mono, stereo): aac_he, aac_he_v2  [default: ""]
      --adjust-for-ipod                          auto adjust bitrate and sampling rate for ipod, if track is to long (may lead to poor quality)
      --name[=NAME]                              provide a custom audiobook name, otherwise the existing metadata will be used [default: ""]
      --sortname[=SORTNAME]                      provide a custom audiobook name, that is used only for sorting purposes [default: ""]
      --album[=ALBUM]                            provide a custom audiobook album, otherwise the existing metadata for name will be used [default: ""]
      --sortalbum[=SORTALBUM]                    provide a custom audiobook album, that is used only for sorting purposes [default: ""]
      --artist[=ARTIST]                          provide a custom audiobook artist, otherwise the existing metadata will be used [default: ""]
      --sortartist[=SORTARTIST]                  provide a custom audiobook artist, that is used only for sorting purposes [default: ""]
      --genre[=GENRE]                            provide a custom audiobook genre, otherwise the existing metadata will be used [default: ""]
      --writer[=WRITER]                          provide a custom audiobook writer, otherwise the existing metadata will be used [default: ""]
      --albumartist[=ALBUMARTIST]                provide a custom audiobook albumartist, otherwise the existing metadata will be used [default: ""]
      --year[=YEAR]                              provide a custom audiobook year, otherwise the existing metadata will be used [default: ""]
      --cover[=COVER]                            provide a custom audiobook cover, otherwise the existing metadata will be used
      --description[=DESCRIPTION]                provide a custom audiobook short description, otherwise the existing metadata will be used
      --longdesc[=LONGDESC]                      provide a custom audiobook long description, otherwise the existing metadata will be used
      --comment[=COMMENT]                        provide a custom audiobook comment, otherwise the existing metadata will be used
      --copyright[=COPYRIGHT]                    provide a custom audiobook copyright, otherwise the existing metadata will be used
      --encoded-by[=ENCODED-BY]                  provide a custom audiobook encoded-by, otherwise the existing metadata will be used
      --series[=SERIES]                          provide a custom audiobook series, this pseudo tag will be used to auto create sort order (e.g. Harry Potter or The Kingkiller Chronicles)
      --series-part[=SERIES-PART]                provide a custom audiobook series part, this pseudo tag will be used to auto create sort order (e.g. 1 or 2.5)
      --skip-cover                               skip extracting and embedding covers
      --fix-mime-type                            try to fix MIME-type (e.g. from video/mp4 to audio/mp4) - this is needed for some players to prevent video window
  -o, --output-dir[=OUTPUT-DIR]                  output directory [default: ""]
  -p, --filename-template[=FILENAME-TEMPLATE]    filename twig-template for output file naming [default: "{{\"%03d\"|format(track)}}-{{title|raw}}"]
      --use-existing-chapters-file               use an existing manually edited chapters file <audiobook-name>.chapters.txt instead of embedded chapters for splitting
  -h, --help                                     Display this help message
  -q, --quiet                                    Do not output any message
  -V, --version                                  Display this application version
      --ansi                                     Force ANSI output
      --no-ansi                                  Disable ANSI output
  -n, --no-interaction                           Do not ask any interactive question
  -v|vv|vvv, --verbose                           Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Help:
  Split an m4b into multiple m4b or mp3 files by chapter

filename-template reference

If you would like to use a custom filename template, the Twig template engine is provided. The following variables are available:

{{encoder}}
{{title}}
{{artist}}
{{genre}}
{{writer}}
{{album}}
{{disk}}
{{disks}}
{{albumArtist}}
{{year}}
{{track}}
{{tracks}}
{{cover}}
{{description}}
{{longDescription}}
{{comment}}
{{copyright}}
{{encodedBy}}
  • You can also use some Twig specific template extensions to pad or reformat these values. The default template is {{\"%03d\"|format(track)}}-{{title}}, which results in filenames like 001-mychapter
  • Slashes are interpreted as directory separators, so if you use a template {{year}}/{{artist}}/{{title}} the resulting directory and file is 2018/Joanne K. Rowling/Harry Potter 1
  • It is not recommended to use {{description}} or {{longdescription}} for filenames but they are also provided, if the field contains other information than intended
  • Special chars, that are forbidden in filenames are removed automatically

chapters

Many m4b audiobook files do not contain valid chapters for different reasons. m4b-tool can handle two cases:

  • Correct misplaced chapters by silence detection
  • Add chapters from an internet source (mostly for well known titles)

Misplaced chapters

In some cases there is a shift between the chapter mark and the real beginning of a chapter. m4b-tool could try to correct that by detecting silences and relocating the chapter to the nearest silence:

m4b-tool chapters --adjust-by-silence -o "data/destination-with-adjusted-chapters.m4b" "data/source-with-misplaced-chapters.m4b"

It won't work, if the shift is to large or if the chapters are strongly misplaced, but since everything is done automatically, it's worth a try, isn't it?

Too long chapters

Sometimes you have a file that contains valid chapters, but they are too long, so you would like to split them into sub-chapters. This is tricky, because the chapters command relays only on metadata and not on track length - so it won't work. BUT: There might be a workaround. In the latest pre-release since July 2020 you can do the following:

  • Put the source file into an empty directory, e.g. input/my-file.m4b (this is important, don't skip this step!)
  • Run m4b-tool merge -v --no-conversion --max-chapter-length=300,900 "input/" -o "output/my-rechaptered-file.m4b"

Because of --no-conversion the chaptering process is lossless, but it takes the existing chapters as input and recalculates it based on the --max-chapter-length parameter and a new silence detection.

No chapters at all

If you have a well known audiobook, like Harry Potter and the Philosopher’s Stone, you might be lucky that it is on musicbrainz.

In this case m4b-tool can try to correct the chapter information using silence detection and the musicbrainz data.

Since this is not a trivial task and prone to error, m4b-tool offers some parameters to correct misplaced chapter positions manually.

A typical workflow

Getting the musicbrainz id

You have to find the exact musicbrainz id:

  • An easy way to find the book is to use the authors name or the readers name to search for it
  • Once you found the book of interest, click on the list entry to show further information
  • To get the musicbrainz id, open the details page and find the MBID (e.g. 8669da33-bf9c-47fe-adc9-23798a37b096)

Example: https://musicbrainz.org/work/8669da33-bf9c-47fe-adc9-23798a37b096

MBID: 8669da33-bf9c-47fe-adc9-23798a37b096

Finding main chapters

After getting the MBID you should find the main chapter points (where the name of the current chapter name is read aloud by the author).

m4b-tool chapters --merge-similar --first-chapter-offset 4000 --last-chapter-offset 3500 -m 8669da33-bf9c-47fe-adc9-23798a37b096 "../data/harry-potter-1.m4b"

Explanation:

  • --merge-similar: merges all similar chapters (e.g. The Boy Who Lived, Part 1 and The Boy Who Lived, Part 2 will be merged to The Boy Who Lived)
  • --first-chapter-offset: creates an start offset chapter called Offset First Chapter with a length of 4 seconds for skipping intros (e.g. audible, etc.)
  • --last-chapter-offset: creates an end offset chapter called Offset Last Chapter with a length of 3,5 seconds for skipping outros (e.g. audible, etc.)
  • -m: MBID

Finding misplaced main chapters

Now listen to the audiobook an go through the chapters. Lets assume, all but 2 chapters were detected correctly. The two misplaced chapters are chapter number 6 and 9.

To find the real position of chapters 6 and 9 invoke:

m4b-tool chapter --find-misplaced-chapters 5,8  --merge-similar --first-chapter-offset 4000 --last-chapter-offset 3500 -m 8669da33-bf9c-47fe-adc9-23798a37b096 "../data/harry-potter-1.m4b"

Explanation: --find-misplaced-chapters: Comma separated list of chapter numbers, that were not detected correctly.

Now m4b-tool will generate a potential chapter for every silence around the used chapter mark to find the right chapter position.

Listen to the audiobook again and find the right chapter position. Note them down.

Manually adjust misplaced chapters

Next run the full chapter detection with the --no-chapter-import option, which prevents writing the chapters directly to the file.

m4b-tool chapter --no-chapter-import --first-chapter-offset 4000 --last-chapter-offset 3500 -m 8669da33-bf9c-47fe-adc9-23798a37b096 "../data/harry-potter-1.m4b"

To Adjust misplaced chapters, do the following:

  • Change the start position of all misplaced chapters manually in the file ../data/harry-potter-1.chapters.txt
  • Import the corrected chapters with mp4chaps -i ../data/harry-potter-1.m4b

Listen to harry-potter-1.m4b again, now the chapters should be at the correct position.

Troubleshooting

If none of the chapters are detected correctly, this can have different reasons:

  • The silence parts of this audiobook are too short for detection. To adjust the minimum silence length, use --silence-min-length 1000 setting the silence length to 1 second.
    • Caution: To low values can lead to misplaced chapters and increased detection time.
  • You provided the wrong MBID
  • There is too much background noise in this specific audiobook, so that silences cannot be detected

Reference

For all options, see m4b-tool chapters --help:

Description:
  Adds chapters to m4b file

Usage:
  chapters [options] [--] <input>

Arguments:
  input                                                      Input file or folder

Options:
      --logfile[=LOGFILE]                                    file to dump all output [default: ""]
      --debug                                                enable debug mode - sets verbosity to debug, logfile to m4b-tool.log and temporary files are not deleted
  -f, --force                                                force overwrite of existing files
      --no-cache                                             do not use cached values and clear cache completely
      --ffmpeg-threads[=FFMPEG-THREADS]                      specify -threads parameter for ffmpeg [default: ""]
      --platform-charset[=PLATFORM-CHARSET]                  Convert from this filesystem charset to utf-8, when tagging files (e.g. Windows-1252, mainly used on Windows Systems) [default: ""]
      --ffmpeg-param[=FFMPEG-PARAM]                          Add argument to every ffmpeg call, append after all other ffmpeg parameters (e.g. --ffmpeg-param="-max_muxing_queue_size" --ffmpeg-param="1000" for ffmpeg [...] -max_muxing_queue_size 1000) (multiple values allowed)
  -a, --silence-min-length[=SILENCE-MIN-LENGTH]              silence minimum length in milliseconds [default: 1750]
  -b, --silence-max-length[=SILENCE-MAX-LENGTH]              silence maximum length in milliseconds [default: 0]
      --max-chapter-length[=MAX-CHAPTER-LENGTH]              maximum chapter length in seconds - its also possible to provide a desired chapter length in form of 300,900 where 300 is desired and 900 is max - if the max chapter length is exceeded, the chapter is placed on the first silence between desired and max chapter length [default: "0"]
  -m, --musicbrainz-id=MUSICBRAINZ-ID                        musicbrainz id so load chapters from
  -s, --merge-similar                                        merge similar chapter names
  -o, --output-file[=OUTPUT-FILE]                            write chapters to this output file [default: ""]
      --adjust-by-silence                                    will try to adjust chapters of a file by silence detection and existing chapter marks
      --find-misplaced-chapters[=FIND-MISPLACED-CHAPTERS]    mark silence around chapter numbers that where not detected correctly, e.g. 8,15,18 [default: ""]
      --find-misplaced-offset[=FIND-MISPLACED-OFFSET]        mark silence around chapter numbers with this offset seconds maximum [default: 120]
      --find-misplaced-tolerance[=FIND-MISPLACED-TOLERANCE]  mark another chapter with this offset before each silence to compensate ffmpeg mismatches [default: -4000]
      --no-chapter-numbering                                 do not append chapter number after name, e.g. My Chapter (1)
      --no-chapter-import                                    do not import chapters into m4b-file, just create chapters.txt
      --chapter-pattern[=CHAPTER-PATTERN]                    regular expression for matching chapter name [default: "/^[^:]+[1-9][0-9]*:[\s]*(.*),.*[1-9][0-9]*[\s]*$/i"]
      --chapter-replacement[=CHAPTER-REPLACEMENT]            regular expression replacement for matching chapter name [default: "$1"]
      --chapter-remove-chars[=CHAPTER-REMOVE-CHARS]          remove these chars from chapter name [default: "„“”"]
      --first-chapter-offset[=FIRST-CHAPTER-OFFSET]          milliseconds to add after silence on chapter start [default: 0]
      --last-chapter-offset[=LAST-CHAPTER-OFFSET]            milliseconds to add after silence on chapter start [default: 0]
  -h, --help                                                 Display this help message
  -q, --quiet                                                Do not output any message
  -V, --version                                              Display this application version
      --ansi                                                 Force ANSI output
      --no-ansi                                              Disable ANSI output
  -n, --no-interaction                                       Do not ask any interactive question
  -v|vv|vvv, --verbose                                       Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug

Help:
  Can add Chapters to m4b files via different types of inputs

Latest release

m4b-tool is a one-man-project, so sometimes it evolves quickly and often nothing happens. If you have reported an issue and it is marked as fixed, there might be no stable release for a long time. That's why now there is a latest tag in combination with a Pre-Release for testing purposes. These releases always contain the most recent builds with all available fixes and new features. Mostly untested, there may be new bugs, non-functional features or - pretty unlikely - critical issues with the risk of data loss. Feedback is always welcome, but don't expect that these are fixed quickly.

To get the Pre-Release, go to https://github.com/sandreas/m4b-tool/releases/tag/latest and download the m4b-tool.tar.gz or if using docker rebuild the image with:

docker build . --build-arg M4B_TOOL_DOWNLOAD_LINK=<link-to-pre-release> -t m4b-tool

Building from source

m4b-tool contains a build script, which will create an executable m4b-tool.phar in the dist folder. Composer for PHP is required, so after installing composer, run following commands in project root folder:

Linux / Unix

Install Dependencies (Ubuntu)

sudo apt install ffmpeg mp4v2-utils fdkaac php-cli composer phpunit php-mbstring

Build

composer install
./build

macOS

Install Dependencies (brew)

brew update
brew install [email protected] phpunit
brew link [email protected]

Build

composer install
./build

Windows

composer install
build

#f03c15 Request for help - especially german users

Right now, I'm experimenting with speech recognition and speech to text using this project

This is for a feature to automatically add chapter names by speech recognition. I'm not sure this will be ever working as expected, but right now I'm pretty confident, it is possible to do the following, if there are enough speech samples in a specific language:

  • Extract chapter names and first sentences of a chapter from an ebook
  • Detect all silences in the audiobook
  • Perform a speech to text for the first 30 seconds after the silence
  • Compare it with the text parts of the ebook, mark the chapter positions and add real chapters names

To do that and improve the german speech recognition, I would really appreciate YOUR help on:

https://voice.mozilla.org/de (german)

No account is needed to help

You can support mozilla DeepSpeech to better support german speech recognition by just verifying sentences after listening or, even more important, reading out loud and uploading sentences. I try to add a few ones every day, its really easy and quite fun. At the moment the german speech recognition is not good enough for the algorithm, but I will check out every now and then - as soon the recognition is working good enough, I'll go on with this feature.

m4b-tool's People

Contributors

2600box avatar adamvoss avatar akirataguchi115 avatar alexanderwillner avatar cpiber avatar dependabot[bot] avatar kpumuk avatar mtdcr avatar numinit avatar sandreas avatar so-business avatar synesso avatar tommorris avatar twoleaves avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

m4b-tool's Issues

Brew formula

It would be convenient to install utility system-wide with brew

Unable to build docker image

Hi,
On debian 9, I'm unable to build the docker image:

---- INSTALL BUILD DEPENDENCIES ----
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/main/x86_64/APKINDEX.tar.gz
WARNING: Ignoring http://dl-cdn.alpinelinux.org/alpine/v3.9/main/x86_64/APKINDEX.tar.gz: temporary error (try again later)
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/community/x86_64/APKINDEX.tar.gz
WARNING: Ignoring http://dl-cdn.alpinelinux.org/alpine/v3.9/community/x86_64/APKINDEX.tar.gz: temporary error (try again later)
ERROR: unsatisfiable constraints:

Error when merging files that have no cover

Hello,

I forgot exactly why I remove the covers before merging files. Perhaps it was some error using --no-conversion that I'm unsure if it remains valid and the step remains required. When first removing covers using AtomicParsley :

for i in *.m4b ; do ; AtomicParsley "$i" --artwork REMOVE_ALL --overWrite ; done

and then merging :

merge . --no-conversion --fix-mime-type --output-file="/Users/.../Desktop/temp/1.m4b"

I get the following error :

extracting cover to /Users/.../Desktop/temp2/....jpg failed

Perhaps m4b-tool assumes there is a cover and then shows as such. Maybe instead a warning or if there's no cover, just be silent ?

MIME type of merge files set as "video/mp4"

Merging .m4b files with original metadata reported by exiftool:

File Type : M4A
File Type Extension : m4a
MIME Type : audio/mp4

results in:

File Type : MP4
File Type Extension : mp4
MIME Type : video/mp4

I'm having trouble getting them to show up as audio in the default iOS Podcasts app. Seems Podcasts thinks they're video files and displays a video window. Is there anyway to fix already merged files rather than having to re-merge? Maybe some other tool?

Fatal error: Uncaught Error: Call to a member function loadRecordings() on null

I installed dependencies with brew on macOS High Sierra 10.13.6, including php v7.1.16.

I tried using the tool to split chapters on silence, and after a while of "detecting silence", this exception was thrown:

$ php m4b-tool.phar chapters -- "My Book.m4b"
detecting silence of My Book.m4b
+++++++++++++++++++++++++++++++++++++++

Fatal error: Uncaught Error: Call to a member function loadRecordings() on null in phar:///Users/me/Downloads/m4b-tool.phar/src/library/M4bTool/Command/ChaptersCommand.php:156
Stack trace:
#0 phar:///Users/me/Downloads/m4b-tool.phar/src/library/M4bTool/Command/ChaptersCommand.php(98): M4bTool\Command\ChaptersCommand->buildChapters()
#1 phar:///Users/me/Downloads/m4b-tool.phar/vendor/symfony/console/Command/Command.php(251): M4bTool\Command\ChaptersCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#2 phar:///Users/me/Downloads/m4b-tool.phar/vendor/symfony/console/Application.php(946): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#3 phar:///Users/me/Downloads/m4b-tool.phar/vendor/symfony/console/Application.php(248): Symfony\Component\Console\Application->doRunCommand(Object(M4bTool\Command\ChaptersCommand), Object(Symf in phar:///Users/me/Downloads/m4b-tool.phar/src/library/M4bTool/Command/ChaptersCommand.php on line 156

Thoughts?

[feature request] performance improvements / multithreading / parallel execution

Just an idea. ffmpeg seems to have a very single threaded encoder for our workloads. Admittedly the speed isn't a huge deal but here is the thought anyways. Why not spawn multiple "worker threads". ffmpeg is the bottleneck so load say 8 or all of the jobs at once and let the commands go.

parameter like --encode-jobs=(number)

Unsure how feasible this is. Just a thought. Other than that I love this tool it saved me alot of time having to potentially write my own

Command "m4b-tool" is not defined.

I tried the docker way on macOS and it results in this error.
Any ideas?

❯ m4b-tool --version
m4b-tool v.0.4.0

❯ m4b-tool merge -v --output-file="output/" --batch-pattern="input/%g/%a/%s/%p - %n/"  --batch-pattern="input/%g/%a/%n/" "input/"

In Application.php line 639:

  [Symfony\Component\Console\Exception\CommandNotFoundException]
  Command "m4b-tool" is not defined.


Exception trace:
 () at phar:///usr/local/bin/m4b-tool/vendor/symfony/console/Application.php:639
 Symfony\Component\Console\Application->find() at phar:///usr/local/bin/m4b-tool/vendor/symfony/console/Application.php:233
 Symfony\Component\Console\Application->doRun() at phar:///usr/local/bin/m4b-tool/vendor/symfony/console/Application.php:145
 Symfony\Component\Console\Application->run() at phar:///usr/local/bin/m4b-tool/bin/m4b-tool.php:32
 require() at /usr/local/bin/m4b-tool:10

Option '--use-existing-chapters-file' not recognized

$ m4b-tool.phar --version
m4b-tool v.0.3.1

I'm trying to use the option --use-existing-chapters-file to specify a chapters file I manually edited, and it gives the following error:

The "--use-existing-chapters-file" option does not exist.

In addition, the description for this feature in the doc/manual seems like it describes a different feature:

--use-existing-chapters-file           adjust chapter position by nearest found silence

Use file name as chapter metadata

M4B-tool should be able to use the file names as chapter titles. A lot of times I have a folder full of properly named files for each chapter, but without any metadata.

Not detecting chapters?

Hi! Thanks for making this.

Sorry if I missed something, but is m4b-tool supposed to read chapters from the .m4b file when doing a split? I get an error when trying to split a file, that it's expecting a chapters.txt, but I expected it to extract that information from the file:

$ m4b-tool split book.m4b
Your ffmpeg version cannot produce top quality aac using encoder aac instead of libfdk_aac
export chapter list of book.m4b

In SplitCommand.php line 69:

  split command assumes that file .\book.chapters.txt exists and is readable

split [-d|--debug] [--debug-filename [DEBUG-FILENAME]] [-f|--force] [--no-cache] [--audio-format [AUDIO-FORMAT]] [--audio-channels [AUDIO-CHANNELS]] [--audio-bitrate [AUDIO-BITRATE]] [--audio-samplerate [AUDIO-SAMPLERATE]] [--audio-codec [AUDIO-CODEC]] [--adjust-for-ipod] [--name [NAME]] [--album [ALBUM]] [--artist [ARTIST]] [--genre [GENRE]] [--writer [WRITER]] [--albumartist [ALBUMARTIST]] [--year [YEAR]] [--cover [COVER]] [--skip-cover] [--use-existing-chapters-file] [--] <input>

The chapters in the file are already correct (ffprobe -i book.m4b -show_chapters shows them)

Split mp3s into better chapters without re-encoding?

Not sure if this is possible yet, but currently I have mp3 audiobooks split into files with long chapters. I like the ability to split chapters smartly on audio silences, but unfortunately my garmin watch doesn't recognize chapters in m4b files. Is there a way to split my mp3 audiobook into more (separate file) chapters without re-encoding anything? It seems like I could probably run merge with the chapter splitting options, then run split again, but I don't see a no-conversion flag for the split tool.

(awesome tool btw, I wish garmin handled the m4b chapters properly, I'd just convert everything with a normal merge if that was the case)

Problem with german Umlauts

Hi,

if the Input or the putput file contains whitespaces the merge process Fails.
Environment: Windows10

with the parameter php m4b-tool.phar merge --output-file="D:\Drachenigel (Die bla Seiner igel 3).m4b" INPUTFOLDER

PHP Notice:  Undefined offset: 1 in phar://D:/Programme/m4b-tool-php/m4b-tool.phar/src/library/M4bTool/Command/AbstractCommand.php on line 202

Notice: Undefined offset: 1 in phar://D:/Programme/m4b-tool-php/m4b-tool.phar/src/library/M4bTool/Command/AbstractCommand.php on line 202

In MergeCommand.php line 324:

  could not get duration for file D:\Drachenigel (Die bla Seiner igel 3)-tmpfiles\1-1-finsihed.m4b

Merge fails with chapters shorter than 1 second

Summary

I'm running m4b-tool built from source in Ubuntu on Windows Subsystem for Linux. When trying to merge a book of mp3 files, the conversion of mp3 chapter files to m4b is successful, but the recombination step fails if an m4b file is less than 1 second long. I believe this is caused by the length metadata tag storing down to seconds rather than hundredths or thousandths of a second. I was able to work around this issue by manually adding a second of silence to the m4b file using audacity (ffmpeg could probably do this too), but I expect there's a more elegant way to do this.

Details

The < 1 sec. length causes $this->meta->inspectExactDuration($file) (src/library/M4bTool/Chapter/ChapterHandler.php, line 98) to return NULL to $duration, which causes $chapter = new Chapter($lastStart, $duration, $chapterName); (src/library/M4bTool/Chapter/ChapterHandler.php, line 99) to throw an error.

Solution

I'm unsure where the inspectExactDuration function comes from, but it could probably be modified such that it handles valid "zero-length" files or calculates length rather than pulling it from metadata.

Trace

Argument 2 passed to M4bTool\Audio\Chapter::__construct() must be an instance of Sandreas\Time\TimeUnit, null given, called in phar:///home/<user>/new/m4b-tool/dist/m4b-tool.phar/src/library/M4bTool/Chapter/ChapterHandler.php on line 99
trace: #0 phar:///home/<user>/new/m4b-tool/dist/m4b-tool.phar/src/library/M4bTool/Chapter/ChapterHandler.php(99): M4bTool\Audio\Chapter->__construct(Object(Sandreas\Time\TimeUnit), NULL, 'MAPS')
#1 phar:///home/<user>/new/m4b-tool/dist/m4b-tool.phar/src/library/M4bTool/Command/MergeCommand.php(445): M4bTool\Chapter\ChapterHandler->buildChaptersFromFiles(Array, Array)
#2 phar:///home/<user>/new/m4b-tool/dist/m4b-tool.phar/src/library/M4bTool/Command/MergeCommand.php(371): M4bTool\Command\MergeCommand->processInputFiles()
#3 phar:///home/<user>/new/m4b-tool/dist/m4b-tool.phar/src/library/M4bTool/Command/MergeCommand.php(165): M4bTool\Command\MergeCommand->processFiles(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#4 phar:///home/<user>/new/m4b-tool/dist/m4b-tool.phar/vendor/symfony/console/Command/Command.php(255): M4bTool\Command\MergeCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#5 phar:///home/<user>/new/m4b-tool/dist/m4b-tool.phar/vendor/symfony/console/Application.php(915): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#6 phar:///home/<user>/new/m4b-tool/dist/m4b-tool.phar/vendor/symfony/console/Application.php(272): Symfony\Component\Console\Application->doRunCommand(Object(M4bTool\Command\MergeCommand), Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#7 phar:///home/<user>/new/m4b-tool/dist/m4b-tool.phar/vendor/symfony/console/Application.php(148): Symfony\Component\Console\Application->doRun(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#8 phar:///home/<user>/new/m4b-tool/dist/m4b-tool.phar/bin/m4b-tool.php(33): Symfony\Component\Console\Application->run()
#9 /home/<user>/new/m4b-tool/dist/m4b-tool.phar(10): require('phar:///home/<user>...')
#10 {main}

Feature Request: Force CBR

When I am using the merge tool with --audio-bitrate=64k, 99% of the time it spits out a file that is of a constant bitrate (CBR). However, occasionally it spits out a file that has a variable bitrate. This typically only happens with books that have longish gaps of silence. The reason this is a problem is that it makes it very difficult to place chapter marks after the fact. Files that are VBR have problems seeking and also have different (reported) duration by differing software. For instance Audacity will report one duration and timestamp and VLC a different duration and different timestamps. I would be awesome if all of this could be avoided by simply having m4b-tool enforce that the output file be CBR.

Output of batch-pattern is a just ".m4b"

The batch-pattern= with output-file= options end up causing a .m4b file with no filename and just the extension in the output. Which would be ok except because it just .m4b its invisible in Finder. In my option it would be great if it was the Book name.m4b in the Author folder or possible even better for the batch-pattern if it was something like -.m4b in the Output folder.

Add support for chapter thumbnails

I think this is not possible now with m4b-tool, but would be a nice feature, if that can be integrated. Guess it can be done using FFmpeg or mp4v2. Tested on VLC (iOS, Android - VLC on Android only offers chapter navigation, if such a thumb track is available, I guess because then it thinks it is a video and handles the file differently) and iTunes/iOS books app - these apps can display these thumb images.

Proposal:

  1. If image is embedded into source audio file, it should be a thumbnail included into the chapter thumb track.
  2. If no image is embedded into source file, a search for filename.[jpg, png]* should be done, an if found, that image should be used as chapter thumbnail.
  3. If creation of chapter thumbnails is activated and no image for a track is found via 1. or 2., a default image or black image should be used. (not sure about this, perhaps creating this slideshow track via FFmpeg offers ability to specify a time range, when an image is to be shown)
  4. Since most players expect these thumb and cover images to be square, it should be possible to crop them or fit them into an empty image with given background color.

Perhaps it can be done like this: https://trac.ffmpeg.org/wiki/Slideshow (edit: this seems to create just a h264 video track, but not a JPEG track)
Somehow it seems to be necessary to tell the mp4 tracks/container that this is a menu track for the main track. Didn't find any info on that topic yet and was not able to produce something useful via FFmpeg by hand.

mediainfo output of the chapter thumbnail track (created via macOS app Audiobook Builder):

Video
ID                                       : 2
Format                                   : JPEG
Codec ID                                 : jpeg
Duration                                 : 7 h 15 min
Bit rate mode                            : Variable
Bit rate                                 : 204 b/s
Width                                    : 512 pixels
Height                                   : 512 pixels
Display aspect ratio                     : 1.000
Frame rate mode                          : Variable
Frame rate                               : 0.001 FPS
Minimum frame rate                       : 0.001 FPS
Maximum frame rate                       : 0.005 FPS
Color space                              : YUV
Chroma subsampling                       : 4:2:0
Bit depth                                : 8 bits
Compression mode                         : Lossy
Bits/(Pixel*Frame)                       : 0.778
Stream size                              : 822 KiB (1%)
Title                                    : Apple Video Mediensteuerung / Apple Alias-Datensteuerung
Language                                 : English
Encoded date                             : UTC 2018-11-19 20:30:36
Tagged date                              : UTC 2018-11-19 20:30:36
Menu For                                 : 1

mp4info output:

2	video	jpeg, 26132.996 secs, 0 kbps, 512x512 @ 0.001263 fps

ffmpeg -i output (Stream #0:1 seems to be the chapter thumb track. The others seem to be metainfo text and the cover image):

    Metadata:
      creation_time   : 2018-11-19T20:30:03.000000Z
      handler_name    : Apple Ton Mediensteuerung
    Stream #0:1(eng): Video: mjpeg (jpeg / 0x6765706A), yuvj420p(pc, bt470bg/unknown/unknown), 512x512 [SAR 72:72 DAR 1:1], 0 kb/s, 0.0013 fps, 600 tbr, 600 tbn, 600 tbc (default)
    Metadata:
      creation_time   : 2018-11-19T20:30:36.000000Z
      handler_name    : Apple Video Mediensteuerung
      encoder         : Foto - JPEG
    Stream #0:2(eng): Data: bin_data (text / 0x74786574)
    Metadata:
      creation_time   : 2018-11-19T20:30:36.000000Z
      handler_name    : Apple Text-Mediensteuerung
    Stream #0:3: Video: mjpeg, yuvj420p(pc, bt470bg/unknown/unknown), 1022x1022 [SAR 144:144 DAR 1:1], 90k tbr, 90k tbn, 90k tbc

Apple lossless codec 'alac' not working

It would be nice, to be able to encode audio books into a lossless codec, so that its possible to make final lossy encode of the audio stream at a later time.

ffmpeg supports Apple lossless codec 'alac'. Trying to produce a result with a command like this:

php ../m4b-tool.phar merge --audio-codec="alac" -d inputFolder --output-file="out.m4b"

results in that:

...
In MergeCommand.php line 328:

  could not convert /Users/test/inputFile.m4a to out-tmpfiles/inputFile.m4b

Its seems the problem is caused by the -f mp4 option, which is set on the final ffmpeg command, resulting in:

[mp4 @ 0x7ff18f00a400] Could not find tag for codec alac in stream #0, codec not currently supported in container
Could not write header for output file #0 (incorrect codec parameters ?): Invalid argument
Error initializing output stream 0:0 --

Omitting the -f mp4 option results in alac encoded m4a file to be correctly generated.

Unable to generate chapters when merging files

On my Win10 64bit Laptop with WSL,
I tried to use the following commad below to merge a set of m4a files in a folder to generate a large m4b file but it did not contain any chapters.
image

m4b-tool merge --no-conversion --use-filenames-as-chapters --no-chapter-reindexing --jobs=8 "./Harry Potter and the Goblet of Fire" --output-file="Harry Potter and the Goblet of Fire.m4b"

No audio/file does not play

Command:

php m4b-tool.phar merge "X:\ToSort\Anthology - Star Wars-- Canto Bight (Read by Multiple Narrators)" --output-file="X:\ToSort\Anthology - Star Wars-- Canto Bight (Read by Multiple Narrators)\Canto Bight.m4b" --ffmpeg-threads=8 --convert-charset --audio-format="m4b" --audio-bitrate="62k" --audio-codec="aac" --artist="Sean Kenin, Saskia Maarleveld, Marc Thompson, Jonathan Davis"  --albumartist="Saladin Ahmed, Rae Carson, Mira Grant, John Jackson Miller" -f -v --no-cache

first file mediainfo

General
Complete name                            : X:\ToSort\Anthology - Star Wars-- Canto Bight (Read by Multiple Narrators)\02 - Saladin Ahmed - Rules of the Game - Chapter 1 (Read by Sean Kenin).m4b
Format                                   : MPEG-4
Format profile                           : Base Media / Version 2
Codec ID                                 : mp42 (isom/mp42/3gp5)
File size                                : 7.09 MiB
Duration                                 : 15 min 27 s
Overall bit rate mode                    : Variable
Overall bit rate                         : 64.1 kb/s
Album                                    : Star Wars: Canto Bight
Track name                               : Rules of the Game - Chapter 1
Track name/Position                      : 2
Performer                                : Saladin Ahmed
Genre                                    : Audiobook - Science Fantasy
Description                              : Soon to be seen in Star Wars: The Last Jedi, welcome to the casino city of Canto Bight. A place where exotic aliens, captivating creatures, and other would-be high rollers are willing to risk everything to make their fortunes. Set across one fateful even
Recorded date                            : 2017
Encoded date                             : UTC 2017-12-07 19:08:29
Tagged date                              : UTC 2017-12-07 19:08:29
Cover                                    : Yes
Comment                                  : Read by Sean Kenin

Audio
ID                                       : 101
Format                                   : AAC
Format/Info                              : Advanced Audio Codec
Format profile                           : LC
Codec ID                                 : mp4a-40-2
Duration                                 : 15 min 27 s
Bit rate mode                            : Variable
Bit rate                                 : 62.8 kb/s
Maximum bit rate                         : 70.4 kb/s
Channel(s)                               : 2 channels
Channel positions                        : Front: L R
Sampling rate                            : 22.05 kHz
Frame rate                               : 21.533 FPS (1024 SPF)
Compression mode                         : Lossy
Stream size                              : 6.95 MiB (98%)
Encoded date                             : UTC 2017-12-07 19:08:29
Tagged date                              : UTC 2017-12-07 19:08:30

Other #1
ID                                       : 2
Type                                     : Object description
Format                                   : System
Codec ID                                 : mp4s-01
Duration                                 : 1 s 0 ms
Encoded date                             : UTC 2017-12-07 19:08:30
Tagged date                              : UTC 2017-12-07 19:08:30

Other #2
ID                                       : 1
Type                                     : Scene description
Format                                   : System Core
Codec ID                                 : mp4s-02
Duration                                 : 1 s 0 ms
Encoded date                             : UTC 2017-12-07 19:08:30
Tagged date                              : UTC 2017-12-07 19:08:30

Merged file mediainfo

General
Complete name                            : X:\ToSort\Anthology - Star Wars-- Canto Bight (Read by Multiple Narrators)\Canto Bight.m4b
Format                                   : MPEG-4
Format profile                           : Base Media
Codec ID                                 : isom (isom/iso2/avc1/mp41)
File size                                : 302 MiB
Duration                                 : 11 h 5 min
Overall bit rate mode                    : Variable
Overall bit rate                         : 63.4 kb/s
Movie name                               : Star Wars: Canto Bight
Album/Performer                          : Saladin Ahmed, Rae Carson, Mira Grant, John Jackson Miller
Performer                                : Sean Kenin, Saskia Maarleveld, Marc Thompson, Jonathan Davis
Genre                                    : Audiobook - Science Fantasy
Recorded date                            : 2017
Tagged date                              : UTC 2018-04-15 06:46:04
Writing application                      : Lavf58.11.101

Video
ID                                       : 1
Format                                   : AVC
Format/Info                              : Advanced Video Codec
Format profile                           : [email protected]
Format settings                          : CABAC / 4 Ref Frames
Format settings, CABAC                   : Yes
Format settings, RefFrames               : 4 frames
Format settings, GOP                     : N=1
Codec ID                                 : avc1
Codec ID/Info                            : Advanced Video Coding
Duration                                 : 11 h 4 min
Bit rate mode                            : Constant
Bit rate                                 : 170 b/s
Width                                    : 500 pixels
Height                                   : 500 pixels
Display aspect ratio                     : 1.000
Frame rate mode                          : Variable
Frame rate                               : 0.001 FPS
Minimum frame rate                       : 0.000 FPS
Maximum frame rate                       : 1 000.000 FPS
Color space                              : YUV
Chroma subsampling                       : 4:2:0
Bit depth                                : 8 bits
Scan type                                : Progressive
Bits/(Pixel*Frame)                       : 0.680
Stream size                              : 1.06 MiB (0%)
Writing library                          : x264 core 155 r2901 7d0ff22
Encoding settings                        : cabac=1 / ref=3 / deblock=1:0:0 / analyse=0x3:0x113 / me=hex / subme=7 / psy=1 / psy_rd=1.00:0.00 / mixed_ref=1 / me_range=16 / chroma_me=1 / trellis=1 / 8x8dct=1 / cqm=0 / deadzone=21,11 / fast_pskip=1 / chroma_qp_offset=-2 / threads=12 / lookahead_threads=2 / sliced_threads=0 / nr=0 / decimate=1 / interlaced=0 / bluray_compat=0 / constrained_intra=0 / bframes=3 / b_pyramid=2 / b_adapt=1 / b_bias=0 / direct=1 / weightb=1 / open_gop=0 / weightp=2 / keyint=250 / keyint_min=25 / scenecut=40 / intra_refresh=0 / rc_lookahead=40 / rc=crf / mbtree=1 / crf=23.0 / qcomp=0.60 / qpmin=0 / qpmax=69 / qpstep=4 / ip_ratio=1.40 / aq=1:1.00
Color range                              : Full
Menus                                    : 3

Audio
ID                                       : 2
Format                                   : AAC
Format/Info                              : Advanced Audio Codec
Format profile                           : LC
Codec ID                                 : mp4a-40-2
Duration                                 : 11 h 5 min
Source duration                          : 11 h 5 min
Bit rate mode                            : Variable
Bit rate                                 : 62.5 kb/s
Maximum bit rate                         : 119 kb/s
Channel(s)                               : 2 channels
Channel positions                        : Front: L R
Sampling rate                            : 22.05 kHz
Frame rate                               : 21.533 FPS (1024 SPF)
Compression mode                         : Lossy
Stream size                              : 297 MiB (99%)
Source stream size                       : 297 MiB (99%)
Default                                  : Yes
Alternate group                          : 1

Menu #1
00:00:00.000                             : Introduction
00:01:49.876                             : Rules of the Game - Chapter 1
00:17:17.791                             : Rules of the Game - Chapter 2
00:27:39.621                             : Rules of the Game - Chapter 3
00:38:31.172                             : Rules of the Game - Chapter 4
00:42:50.538                             : Rules of the Game - Chapter 5
00:57:33.314                             : Rules of the Game - Chapter 6
01:12:20.362                             : Rules of the Game - Chapter 7
01:29:38.897                             : Rules of the Game - Chapter 8
01:33:51.344                             : Rules of the Game - Chapter 9
01:40:58.916                             : Rules of the Game - Chapter 10
01:45:44.428                             : Rules of the Game - Chapter 11
02:01:56.508                             : The Wine in Dreams - Chapter 1
02:09:48.616                             : The Wine in Dreams - Chapter 2
02:22:55.075                             : The Wine in Dreams - Chapter 3
02:46:52.390                             : The Wine in Dreams - Chapter 4
02:54:19.699                             : The Wine in Dreams - Chapter 5
03:02:09.531                             : The Wine in Dreams - Chapter 6
03:18:00.806                             : The Wine in Dreams - Chapter 7
03:48:33.139                             : The Wine in Dreams - Chapter 8
03:57:07.135                             : The Wine in Dreams - Chapter 9
04:22:17.871                             : The Wine in Dreams - Chapter 10
04:47:29.397                             : The Wine in Dreams - Chapter 11
04:52:10.590                             : Hear Nothing, See Nothing, Say Nothing - Chapter 1
05:04:55.780                             : Hear Nothing, See Nothing, Say Nothing - Chapter 2
05:40:35.220                             : Hear Nothing, See Nothing, Say Nothing - Chapter 3
05:55:51.247                             : Hear Nothing, See Nothing, Say Nothing - Chapter 4
06:05:16.838                             : Hear Nothing, See Nothing, Say Nothing - Chapter 5
06:34:16.198                             : Hear Nothing, See Nothing, Say Nothing - Chapter 6
06:51:56.839                             : Hear Nothing, See Nothing, Say Nothing - Chapter 7
07:16:13.984                             : Hear Nothing, See Nothing, Say Nothing - Chapter 8
07:23:49.373                             : Hear Nothing, See Nothing, Say Nothing - Chapter 9
07:41:24.534                             : Hear Nothing, See Nothing, Say Nothing - Chapter 10
07:53:59.368                             : The Ride - The Grinder - 1
08:08:39.218                             : The Ride - The Trips - 2
08:18:21.342                             : The Ride - The Gutshot - 3
08:29:57.987                             : The Ride - The Bad Beat - 4
08:40:20.746                             : The Ride - The Sharp - 5
08:51:04.867                             : The Ride - The Bust Out - 6
08:59:07.656                             : The Ride - The Edge - 7
09:06:48.572                             : The Ride - The Buy-in - 8
09:18:32.461                             : The Ride - The Tell - 9
09:26:51.968                             : The Ride - The Stretch - 10
09:41:57.406                             : The Ride - The Turn - 11
09:50:23.368                             : The Ride - The Wire - 12
10:03:10.741                             : The Ride - The Breakdown - 13
10:15:24.398                             : The Ride - The Surrender - 14
10:25:03.085                             : The Ride - The Freeroll - 15
10:36:03.692                             : The Ride - The Showdown - 16
10:45:15.073                             : The Ride - The Kicker - 17
10:55:01.376                             : The Ride - The Rake - 18
11:04:44.754                             : Credits & Copyright

Menu #2
ID                                       : 3
Codec ID                                 : text
Duration                                 : 11 h 4 min
Encoded date                             : UTC 2018-04-15 06:45:54
Tagged date                              : UTC 2018-04-15 06:45:54
Menu For                                 : 1
00:00:00.000                             : Introduction
00:01:49.876                             : Rules of the Game - Chapter 1
00:17:17.791                             : Rules of the Game - Chapter 2
00:27:39.621                             : Rules of the Game - Chapter 3
00:38:31.172                             : Rules of the Game - Chapter 4
00:42:50.538                             : Rules of the Game - Chapter 5
00:57:33.314                             : Rules of the Game - Chapter 6
01:12:20.362                             : Rules of the Game - Chapter 7
01:29:38.897                             : Rules of the Game - Chapter 8
01:33:51.344                             : Rules of the Game - Chapter 9
01:40:58.916                             : Rules of the Game - Chapter 10
01:45:44.428                             : Rules of the Game - Chapter 11
02:01:56.508                             : The Wine in Dreams - Chapter 1
02:09:48.616                             : The Wine in Dreams - Chapter 2
02:22:55.075                             : The Wine in Dreams - Chapter 3
02:46:52.390                             : The Wine in Dreams - Chapter 4
02:54:19.699                             : The Wine in Dreams - Chapter 5
03:02:09.531                             : The Wine in Dreams - Chapter 6
03:18:00.806                             : The Wine in Dreams - Chapter 7
03:48:33.139                             : The Wine in Dreams - Chapter 8
03:57:07.135                             : The Wine in Dreams - Chapter 9
04:22:17.871                             : The Wine in Dreams - Chapter 10
04:47:29.397                             : The Wine in Dreams - Chapter 11
04:52:10.590                             : Hear Nothing, See Nothing, Say Nothing - Chapter 1
05:04:55.780                             : Hear Nothing, See Nothing, Say Nothing - Chapter 2
05:40:35.220                             : Hear Nothing, See Nothing, Say Nothing - Chapter 3
05:55:51.247                             : Hear Nothing, See Nothing, Say Nothing - Chapter 4
06:05:16.838                             : Hear Nothing, See Nothing, Say Nothing - Chapter 5
06:34:16.198                             : Hear Nothing, See Nothing, Say Nothing - Chapter 6
06:51:56.839                             : Hear Nothing, See Nothing, Say Nothing - Chapter 7
07:16:13.984                             : Hear Nothing, See Nothing, Say Nothing - Chapter 8
07:23:49.373                             : Hear Nothing, See Nothing, Say Nothing - Chapter 9
07:41:24.534                             : Hear Nothing, See Nothing, Say Nothing - Chapter 10
07:53:59.368                             : The Ride - The Grinder - 1
08:08:39.218                             : The Ride - The Trips - 2
08:18:21.342                             : The Ride - The Gutshot - 3
08:29:57.987                             : The Ride - The Bad Beat - 4
08:40:20.746                             : The Ride - The Sharp - 5
08:51:04.867                             : The Ride - The Bust Out - 6
08:59:07.656                             : The Ride - The Edge - 7
09:06:48.572                             : The Ride - The Buy-in - 8
09:18:32.461                             : The Ride - The Tell - 9
09:26:51.968                             : The Ride - The Stretch - 10
09:41:57.406                             : The Ride - The Turn - 11
09:50:23.368                             : The Ride - The Wire - 12
10:03:10.741                             : The Ride - The Breakdown - 13
10:15:24.398                             : The Ride - The Surrender - 14
10:25:03.085                             : The Ride - The Freeroll - 15
10:36:03.692                             : The Ride - The Showdown - 16
10:45:15.073                             : The Ride - The Kicker - 17
10:55:01.376                             : The Ride - The Rake - 18
11:04:44.754                             : Credits & Copyright
Bit rate mode                            : VBR

mp3 source files with title tag issue

I'm trying to use m4b-tool to merge mp3 files to an m4b. The mp3 files definitely have the title tag set but when I merge, in the m4b I just get sequential numbering (1,2,3 etc).

I'm using:

m4b-tool merge audiobook_path --output-file=test.m4b

Am I missing something super obvious or does m4b-tool not work with mp3 tag data? - Thanks

build.phar incompatible with PHP 7.2

On PHP 7.2 (I tested with 7.2.3), build.phar gives the message:

[ErrorException] count(): Parameter must be an array or an object that implements Countable

Conversion fails if chapters.txt file does not exist

Hello,

On macOS High Sierra, using m4b-tool 0.4.2 installed using MacPorts, in a folder of AIFF files with no tag metadata, I run:

$ m4b-tool merge --include-extensions=aiff --output-file=test.m4b .

The error I get is:

extracting cover to ./cover.jpg failed
Argument 2 passed to M4bTool\Audio\Chapter::__construct() must be an instance of Sandreas\Time\TimeUnit, null given, called in phar:///opt/local/bin/m4b-tool/src/library/M4bTool/Chapter/ChapterHandler.php on line 95

If I create an empty file called chapters.txt in the same directory, then m4b-tool succeeds.

Could not convert…

With every folder I try, I get an error message similar to this one:

In MergeCommand.php line 323:

  could not convert E:\Users\Mark\Downloads\Audiobooks\Annihilation\001.mp3 to E:\Users\Mark\Downloads\Audiobooks\Annihilation-tmpfiles\1-001-converting.m4b

How can I diagnose the issue?

Help output in readme is out of date

The output of m4b-tool merge --help that is shown in the readme is outdated. For example, it does not mention the --use-filenames-as-chapters or --no-chapter-reindexing options.

Merged files with incorrect chapter marks if extension m4a is used

I've yet to investigate what makes this happen but on one set of merged files, I have numerous instances of incorrect chapter marks.

In this case, the files are sourced from Audible. DRM removed losslessly. Their codec is AAC, around 62 kpbs bitrate, possibly constrained VBR but that's just a guess. I've heard VBR isn't recommended for podcasts since it can cause seeking problems. Unsure if that is a factor. Mp4chaps has an open pull request that might be related, unsure.

Opening up this issue before listing details and trying to find necessary info to document. Recommended steps? Without sharing files, maybe using some tool to list original file lengths and then chapters file from merge?

Memory leak during large Batch audiobook conversion

I have about 100 audiobooks organised in folders structures and am using the batch conversion to create .m4b files but it runs out of memory during this process.

[mp3float @ 0x5645ffd84100] overread, skip -6 enddists: -4 -4
[mp3float @ 0x5645ffd84100] overread, skip -7 enddists: -6 -6
frame= 1 fps=0.0 q=-0.0 Lsize=N/A time=00:41:14.05 bitrate=N/A speed=79.5x
video:1kB audio:213098kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown

PHP Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 67108872 bytes) in phar:///usr/local/bin/m4b-tool/src/library/M4bTool/StringUtilities/Runes.php on line 26
an error occured, that has not been caught:
Array
(
[type] => 1
[message] => Allowed memory size of 134217728 bytes exhausted (tried to allocate 67108872 bytes)
[file] => phar:///usr/local/bin/m4b-tool/src/library/M4bTool/StringUtilities/Runes.php
[line] => 26
)

Multithreading

Just curious, is this tool multithreaded at all? Could it benefit from multithreading? I noticed that each m4b file when merging is created one at a time -- since I have 8 cores, could I potentially be creating 8 mb4 files at a time? Or is ffmpeg already multithreaded...

Curious to hear your thoughts.

Feature Request: Import/export just opf and cover.jpg without merge

There may be a way to do this already that I am overlooking. With mp4chaps you can import and export the chapters.txt file without the long merge process (useful if you already have a merged m4b file and need to adjust chapter times/names). It would be cool if you could do the same with the cover and metadata.

Fatal Error: Uncaught TypeError: Argument 3 passed to M4bTool\Chapter\ChapterMarker::guessChaptersBySilences() must be an instance of Sandreas\Time\TimeUnit

I am trying to put chapters from musicbrainz in a m4b.

This is the command:
m4b-tool chapters --merge-similar --first-chapter-offset 4000 --last-chapter-offset 3500 -m 969d83a3-e80c-4b47-895a-5b8a410836bd "/mnt/d/Download/Star Wars Erben des Imperiums Hörbuch (Band 1).m4b"

And I am constantly getting this fatal error:
PHP Fatal error: Uncaught TypeError: Argument 3 passed to M4bTool\Chapter\ChapterMarker::guessChaptersBySilences() must be an instance of Sandreas\Time\TimeUnit, null given, called in phar:///usr/local/bin/m4b-tool/src/library/M4bTool/Command/ChaptersCommand.php on line 177 and defined in phar:///usr/local/bin/m4b-tool/src/library/M4bTool/Chapter/ChapterMarker.php:33
Stack trace:
#0 phar:///usr/local/bin/m4b-tool/src/library/M4bTool/Command/ChaptersCommand.php(177): M4bTool\Chapter\ChapterMarker->guessChaptersBySilences(Array, Array, NULL)
#1 phar:///usr/local/bin/m4b-tool/src/library/M4bTool/Command/ChaptersCommand.php(128): M4bTool\Command\ChaptersCommand->buildChapters(Array)
#2 phar:///usr/local/bin/m4b-tool/vendor/symfony/console/Command/Command.php(255): M4bTool\Command\ChaptersCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#3 phar:///usr/local/bin/m4b-tool/vendor/symfony/console/Application.php(908): Symfony\Component\Console\Command\Command-> in phar:///usr/local/bin/m4b-tool/src/library/M4bTool/Chapter/ChapterMarker.php on line 33
an error occured, that has not been caught:
Array
(
[type] => 1
[message] => Uncaught TypeError: Argument 3 passed to M4bTool\Chapter\ChapterMarker::guessChaptersBySilences() must be an instance of Sandreas\Time\TimeUnit, null given, called in phar:///usr/local/bin/m4b-tool/src/library/M4bTool/Command/ChaptersCommand.php on line 177 and defined in phar:///usr/local/bin/m4b-tool/src/library/M4bTool/Chapter/ChapterMarker.php:33
Stack trace:
#0 phar:///usr/local/bin/m4b-tool/src/library/M4bTool/Command/ChaptersCommand.php(177): M4bTool\Chapter\ChapterMarker->guessChaptersBySilences(Array, Array, NULL)
#1 phar:///usr/local/bin/m4b-tool/src/library/M4bTool/Command/ChaptersCommand.php(128): M4bTool\Command\ChaptersCommand->buildChapters(Array)
#2 phar:///usr/local/bin/m4b-tool/vendor/symfony/console/Command/Command.php(255): M4bTool\Command\ChaptersCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#3 phar:///usr/local/bin/m4b-tool/vendor/symfony/console/Application.php(908): Symfony\Component\Console\Command\Command->
[file] => phar:///usr/local/bin/m4b-tool/src/library/M4bTool/Chapter/ChapterMarker.php
[line] => 33
)

I think it is very weird because this command is not using the guessChaptersBySilences() function.

I am using Ubuntu in WSL on Windows 10. I've installed all dependencies, but I am using a customized ffmpeg with all non free codecs installed. Although this should not have any impact on this issue

Thanks for creating this wonderful wrapper :)

m4b-tool php parse

Title: m4b-tool does not give a vaild install version - array syntax error

First of all I just want to say that this looks like a first class job, I'm so grateful that people like you take the time to write stuff like this.......he says bowing down in a Waynes World type "we're not worthy" salute........

If i run m4b-tool --version I receive a PHP Parse Error as follows.

OS: Ubuntu 16.04.6 LTS
Command: m4b-tool --version
Error:

PHP Parse error:  syntax error, unexpected '?', expecting variable (T_VARIABLE) in phar:///usr/local/bin/m4b-tool/vendor/symfony/console/Output/Output.php on line 40
an error occured, that has not been caught:
Array
(
    [type] => 4
    [message] => syntax error, unexpected '?', expecting variable (T_VARIABLE)
    [file] => phar:///usr/local/bin/m4b-tool/vendor/symfony/console/Output/Output.php
    [line] => 40
)

Metadata not written to mp3 files

I understand that the primary output for m4b-tool is m4b files, but the merge command does support mp3 output. When I try to merge a directory of already tagged mp3 files into a single mp3 file with chapters, I end up with an mp3 file with no tags (not even Artist, title, cover, etc.).

I have tried several variations, including defining output format, codec, --no-convert, --mark-chapters, etc. It appears that the tagging step is just skipped if the output format is mp3. I did a test build of a m4b file and it was properly tagged and chaptered.

If this tool doesn't support tagging output mp3s, I can completely understand, I just wanted to confirm I was not missing something.

How to add custom ffmpeg options

Great peace of software, that makes all those mostly unuseable gui apps out there obsolete. Thanks.

I would like to make use of High-Efficiency AAC, since output at 16K bitrate is much better. https://trac.ffmpeg.org/wiki/Encode/AAC#fdk_he

This has already been implemented, https://github.com/sandreas/m4b-tool/blob/master/src/library/M4bTool/Command/MergeCommand.php#L297

How can I add additional options, like -profile:a aac_he -cutoff 18000, that get forwarded to FFmpeg?

Add MP4 input support

I would really appreciate if you could add MP4 input support. I often convert video products into audio books, so I can listen to them on the go.

This should be really simple to add, as M4B-tool already works if you just rename the input .mp4 files to .aac .

Merge multiple m4b files without transcoding?

This seems to re-encode all the m4b files in the input folder, when I just want to merge them into a single m4b.

Does an option to skip the unnecessary re-encoding exist or is that feature not implemented yet?

File order issues

As a general request, add input option for input file ordering.

More specifically, I am attempting to do something like the following:

m4b-tool merge --mark-tracks "file 1" "file 2" "another file" --output-file out.m4b

Now, the conversion and output are OK, and there are chapter marks in the output file, but...

  1. Chapter ORDER is incorrect - it appears the tool uses ALPHABETICAL ordering ("another file" placed before "file 1")
  2. Chapter NAME - Here the tool names the chapter using something like "01 - another file - finished", "02 - file 1 - finished", etc. Why is the "-finished" terminator being used?

For the input ORDER, as a minimum, it would be useful to have a flag to "preserve-order", meaning whatever the order of the input files on the command line, keep that order in the final file. I have tried to artificially put song/track numbers in the MP3s to force the order, but this seems to be ignored.

Conversion fails if output file is not specified

Hello,

I'm trying to use m4b-tool for the first time and I'm encountering a few problems. I'm on macOS High Sierra and I've installed m4b-tool 0.4.2 using a MacPorts Portfile I just wrote. (I'll submit that Portfile into the official MacPorts repository once I'm sure m4b-tool is working.)

I have a folder of AIFF files (/private/tmp/m4b-test). The files don't have any tag metadata. In that folder, I run:

$ m4b-tool merge --include-extensions=aiff .

The error is:

extracting cover to ./cover.jpg failed
could not convert /private/tmp/m4b-test/1.aiff to -tmpfiles/1-1-finished.m4b

If I specify --output-file test.m4b then I can get past that error (and on to another one, for which I'll file a separate issue).

Problem with large mp3 input files when using the fdkaac profile

I found I needed to split very large mp3 files e.g. if it was great than 4 hours the fdkaac would fail and leave a very very large PCM tmp file around. To solve this I have been looking at splitting the large mp3 into smaller ones which seems to solve the problem - the smaller ones later get merged back into a single large m4b fine - its just the initial transcoding that seems to fail with fdkaac.

Chapters are off

Just merged an audiobook with many files (>130). Unfortunately the chapter markers are a bit off.
At the beginning of the audiobook it's just a little bit, but the chapters towards the end are off by a few seconds.

PHP Fatal error: Uncaught Error: Call to undefined function M4bTool\Parser\simplexml_load_string()

While trying to add chapters to an m4b audiobook with this command:

m4b-tool chapters --first-chapter-offset 4000 --last-chapter-offset 3500 -m 969d83a3-e80c-4b47-895a-5b8a410836bd "/mnt/d/Download/Star Wars Erben des Imperiums Hörbuch (Band 1).m4a"

I've got this error:

PHP Fatal error: Uncaught Error: Call to undefined function M4bTool\Parser\simplexml_load_string() in phar:///usr/local/bin/m4b-tool/src/library/M4bTool/Parser/MusicBrainzChapterParser.php:85
Stack trace:
#0 phar:///usr/local/bin/m4b-tool/src/library/M4bTool/Command/ChaptersCommand.php(125): M4bTool\Parser\MusicBrainzChapterParser->parseRecordings('<?xml version="...')
#1 phar:///usr/local/bin/m4b-tool/vendor/symfony/console/Command/Command.php(255): M4bTool\Command\ChaptersCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#2 phar:///usr/local/bin/m4b-tool/vendor/symfony/console/Application.php(908): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#3 phar:///usr/local/bin/m4b-tool/vendor/symfony/console/Application.php(269): Symfony\Component\Console\Application->doRunCommand(Object(M4bTool\Command\ChaptersCommand), Object(Symfony\Component\Console\ in phar:///usr/local/bin/m4b-to
ol/src/library/M4bTool/Parser/MusicBrainzChapterParser.php on line 85
an error occured, that has not been caught:
Array
(
[type] => 1
[message] => Uncaught Error: Call to undefined function M4bTool\Parser\simplexml_load_string() in phar:///usr/local/bin/m4b-tool/src/library/M4bTool/Parser/MusicBrainzChapterParser.php:85
Stack trace:
#0 phar:///usr/local/bin/m4b-tool/src/library/M4bTool/Command/ChaptersCommand.php(125): M4bTool\Parser\MusicBrainzChapterParser->parseRecordings('<?xml version="...')
#1 phar:///usr/local/bin/m4b-tool/vendor/symfony/console/Command/Command.php(255): M4bTool\Command\ChaptersCommand->execute(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#2 phar:///usr/local/bin/m4b-tool/vendor/symfony/console/Application.php(908): Symfony\Component\Console\Command\Command->run(Object(Symfony\Component\Console\Input\ArgvInput), Object(Symfony\Component\Console\Output\ConsoleOutput))
#3 phar:///usr/local/bin/m4b-tool/vendor/symfony/console/Application.php(269): Symfony\Component\Console\Application->doRunCommand(Object(M4bTool\Command\ChaptersCommand), Object(Symfony\Component\Console
[file] => phar:///usr/local/bin/m4b-tool/src/library/M4bTool/Parser/MusicBrainzChapterParser.php
[line] => 85
)

But I solved it by installing the php7.2-xml extension. Maybe you should put this as a dependency in the Installation Description of Ubuntu. I am using the WSL based on ubuntu.

Symlinks are ignored

Using m4b-tool 0.4.2 installed using MacPorts on macOS High Sierra, I'm trying to create an m4b file from a folder of AIFF files. But these files don't have any silence at the beginning or end, so combining them as-is results in one chapter running immediately into the next, which isn't very pleasant. One wants a couple seconds of silence between chapters. To that end, I've used sox to create a 2-second-long silence file which I would like to place between each chapter. The way that occurred to me to do that was to create several symlinks to the silence file:

$ ls -l source/
total 288
lrwxr-xr-x  1 rschmidt  wheel     26 Sep 26 18:27 0s.aiff -> ../silence/silence0.5.aiff
-rw-r--r--  1 rschmidt  staff  84504 Sep 24 18:32 1.aiff
lrwxr-xr-x  1 rschmidt  wheel     26 Sep 26 18:27 1s.aiff -> ../silence/silence2.0.aiff
-rw-r--r--  1 rschmidt  staff  22760 Sep 24 18:32 2.aiff
lrwxr-xr-x  1 rschmidt  wheel     26 Sep 26 18:27 2s.aiff -> ../silence/silence2.0.aiff
-rw-r--r--  1 rschmidt  staff  21112 Sep 24 18:32 3.aiff
lrwxr-xr-x  1 rschmidt  wheel     26 Sep 26 18:27 3s.aiff -> ../silence/silence2.0.aiff
-rw-r--r--  1 rschmidt  staff  26152 Sep 24 18:32 4.aiff
lrwxr-xr-x  1 rschmidt  wheel     26 Sep 26 18:27 4s.aiff -> ../silence/silence2.0.aiff
-rw-r--r--  1 rschmidt  staff  32728 Sep 24 18:32 5.aiff
lrwxr-xr-x  1 rschmidt  wheel     26 Sep 26 18:27 5s.aiff -> ../silence/silence2.0.aiff
-rw-r--r--  1 rschmidt  staff  21408 Sep 24 18:32 6.aiff
lrwxr-xr-x  1 rschmidt  wheel     26 Sep 26 18:28 6s.aiff -> ../silence/silence2.0.aiff
-rw-r--r--  1 rschmidt  staff  33784 Sep 24 18:32 7.aiff
lrwxr-xr-x  1 rschmidt  wheel     26 Sep 26 18:28 7s.aiff -> ../silence/silence2.0.aiff
-rw-r--r--  1 rschmidt  staff  31804 Sep 24 18:32 8.aiff
-rw-r--r--  1 rschmidt  wheel     29 Sep 26 18:19 chapters.txt

But this doesn't work: m4b-tool ignores the symlinks.

If I copy the silence files instead of making symlinks, that works, but of course that takes up a little more disk space. I suppose I could use hardlinks. But the point is that it was unexpected that symlinks were silently ignored.

ChapterTitleBuilder.php crash

In ChapterTitleBuilder.php line 42:
                                                                           
  could not get duration for file data/merged-tmpfiles/01-01-finished.m4b  
                                                                           

I am running Arch Linux all i did was run the tool and it worked up until it threw this error unsure what I did wrong.

just ran
m4b-tool merge $(some input path) --output-file=$(some output path)

charset windows-1252 is not supported - use one of these instead: utf-8

Running latest beta on windows, I get the following:

mp4tools>php m4b-tool.phar merge "X:\ToSort\Jeff VanderMeer - Borne" --output-file "X:\ToSort\Borne.m4b" --ffmpeg-threads=4 --convert-charset
.
.
.
getting duration for X:\ToSort\Borne-tmpfiles\37-01 - Chapter 1-finished.m4b


  [Exception]
  charset windows-1252 is not supported - use one of these instead: utf-8

Get error "Input is not a file" using docker image

What an amazing project you have put together.

Issue

After building the docker image without any issue, I am not able to split an m4b file

Following error is thrown:

[maarten@zbook m4b-tool]$ m4b-tool split --audio-format aac data/issue-120.m4b 

In AbstractCommand.php line 525:
                       
  Input is not a file

Issue

Background, I am adding MP4 chapter support in music-metadata. Which I believe is working. Problem is, it makes most of my MP4 sample useless. I stripped of the MDAT payload of most of the files, and maybe some more, to get to workable size. I was hoping my splitting, trimming and joining, I could shrink the originals, in such a way there are chapters, but the audio content is limited to something like a a second.

Merging does not work on OSX

Running php m4b-tool.phar merge on 10.12 and PHP 7.2 results in:

  [Exception]
  DateTime::__construct(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.time
  zone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting
  this warning, you most likely misspelled the timezone identifier. We selected the timezone 'UTC' for now, but please set d
  ate.timezone to select your timezone.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.