This is a maintained fork of myxdvz/booktree. It keeps booktree's file layout and run-log format so existing setups keep working, and fixes the matching, caching and OPF defects listed in docs/FORK.md. Image:
ghcr.io/mancolt/booktree:latest. Issues and pull requests are welcome; see CONTRIBUTING.md and SECURITY.md.Releases:
:latest,:3.0.0and:3.0are published from tagged releases (v*, see CHANGELOG.md); every merge tomainpublishes:edge, which is untested beyond CI. Pin a version tag in anything unattended.
Reorganize your audiobooks using ID3 or Audible metadata into a tree structure recommended and supported by media servers like Audibookshelf. The originals are untouched and will be hardlinked to their destination
It does the following:
- take a source folder, ideally your downloads folder where your audiobook files are
- recursively find all the M4B/MP3 files in it, and for each file:
- pull and parse metadata information from id3 tags
- using the id3 tags and the file information, attempt to pull metadata from the Metadata sources
- create a tree structure on the target folder, ideally your media folder (like your abs audiobook library folder)
- hardlink the audiobook file to the target folder
booktree builds the following heirarchy on the target folder (this is configurable):
- <media_path>/Author/Title (If there is no series information)
- <media_path>/Author/Series/Series #Part - Title
The above format is the default. User can modify/tweak this in the config file. See Config File Documentation
to run in the docker container, run: > docker exec [-it] <<container_name>> /venv/bin/python booktree.py /config/<<config>>.json
usage: booktree [-h] [--dry-run] config_file
Reorganize your audiobooks using ID3 or Audbile metadata. The originals are untouched and will be hardlinked to their
destination
positional arguments:
config_file Your Config File
options:
-h, --help show this help message and exit
--dry-run If provided, will override dryRun in config
- Python >= 3.10
- ffmpeg
- httpx
- thefuzz
- pathvalidate
- Requests
- langcodes
-
run pip install -r requirements.txt to install dependencies
-
copy default_config.cfg into config.json and modify with your paths settings (files, source_path, media_path)
-
if using MAM as a source, create a MAM session ID (MAM Preferences > Security) and give it to booktree once, in one of these ways (see CONFIG.md):
- the environment variable
MAM_SESSION, orMAM_SESSION_FILEpointing at a docker/compose secret file, withsessionleft blank in every config file; - a mousehole state file mounted read-only and named in
Config/mousehole_state_file(orMOUSEHOLE_STATE_FILE), if mousehole keeps your MAM session alive; /Config/sessionin the config file, as upstream did.
After a successful check booktree keeps the cookie MAM handed back in
<log_path>/cookies.json(owner-readable), so a rotated cookie survives between runs. The value is never printed. - the environment variable
booktree exits 0 when every configured path was processed, 2 for a configuration or input problem, 1 for an unhandled error (CONFIG.md).
For a download-client hook or a timer, booktree itself can do what a wrapper script otherwise does (all off until configured, see CONFIG.md):
notify: post the run summary (matched/unmatched counts, the unmatched releases, the log path) to an ntfy topic, always or only when something needs attention, and ping a heartbeat URL after a clean run;abs: ask Audiobookshelf to scan the library when new hardlinks were made;dedupe_roots: do not hardlink a book whose files are already filed in a listed library.
booktree files each release once and never touches Audiobookshelf's database, so a wrong match is not re-applied on later runs and a correction is not undone. Wrong metadata only: use Audiobookshelf's Match. Wrong book, or no match at all: one line, no log editing:
booktree.py /Config/config.json --pin "Author - Title=B0C5Q9XJ1K" --remember
The release is re-filed under the right name with the right OPF; --remember keeps the correction in your hints
file for future runs. Remove the old, wrong folder by hand (booktree never deletes). Details in
CONFIG.md.
- Start small (pick a folder that has a handful of books, don't run it on 2K files the first try :) )
- Run booktree in --dry-run mode
- Check the resulting log file to check the matches. What you should check for:
- Rows where isMatched = TRUE
- Anywhere mamCount = 1 is an exact match... celebrate!
- Check for rows where mamCount or audibleMatchCount is high (>3), if it is, just check if it picked the right match
- Rows where isMatched = FALSE - there are many reasons why there won't be a match
- The book is NOT SOLD on Audible at all (or in your region)
- The book/torrent has been deleted since you snatched it
- The ID3 metadata is empty or bad, e.g., Author/Narrator that's not comma delimited, bad title and series information
- Rows where isMatched = TRUE
- If everything looks good, rerun booktree without the --dry-run parameter
- Recategorize/Set Location (in you client, e.g., Qbit), to where you have your "processed" files to optimize performance. It's ok if you don't, the script will add them to the list of files to be processed, but will skip processing them if they have already been processed before (cache check).
Optionally, you can choose to work on the log file, and feed that as input to booktree in a succeeding run:
- Fix the paths column to edit/change the generated target path. When isMatched=TRUE, booktree will just use the paths value as-is
- If isMatched = FALSE, you can fix the id3-metadata to re-do the search. The areas to focus on are:
- id3-asin
- id3-title
- id3-author
- id3-seriesparts
- Rerun booktree using the "log" mode and passing the updated logfile as input, booktree.py log /config/log_config.json. I recommend having a separate log_config.json file for this
- It should work seamlessly on any single file or multi-file book under a single book folder
- The script may not immediately work on older, multibook collections >> set multibook = true
- The script may not immediately work on Multi-CD books
- Hard linking will only work if the source and target paths are on the same volume. If you are using Unraid, same datasets
Q: Where is my config file?
A: You can copy the default_config.cfg into .json. Modify or add the values of paths: [{file, source_path, media_path}]
Q: My files are from other sources, can I still use this tool?
A: Use audible as metadata source, Config/metadata = audible
Q: What if the mam or audible search returns multiple matches?
A: Fuzzymatch is used to get the best match
Q: My metadata is not producing any match, what can I do?
A: Lower the matchrate, Change the fuzzy_match algorith, Set --fixid3 flag.