Organize downloaded audiobooks into Audiobookshelf library layout
Project description
abs-organize
CLI to place downloaded audiobooks into an Audiobookshelf library layout from embedded tags (and optional folder-name guesses). Copy is the default; use --move to clear the inbox after a successful run.
Layout: {library}/{Author}/[{Series}/]{TitleFolder}/
Requirements: Python 3.11+
Quick start
pip install abs-organize
# One-off (no config file)
abs-organize ~/Downloads/book.m4b --library ~/Audiobooks --dry-run
abs-organize ~/Downloads/book.m4b --library ~/Audiobooks
# With config (see Configuration)
abs-organize ~/Downloads/inbox/MyBook.m4b
Install
From PyPI (recommended):
pip install abs-organize
From a clone:
pip install -e .
For development and tests, see Development.
Usage
abs-organize INPUT [options]
INPUT — one audio file (.mp3, .m4b, .m4a, .flac, .ogg) or a folder of tracks.
| Option | Purpose |
|---|---|
--library PATH |
Library root for this run (overrides config and env) |
--profile NAME |
Named [libraries.*] profile (default profile when omitted) |
--dry-run |
Show library, destination, and planned ops; no writes |
--move |
Move into the library instead of copy (rename on same FS) |
--replace |
Delete existing destination title folder, then organize |
--allow-guess |
Guess author/title from folder or file name when tags are missing |
--batch |
Organize every detected book under INPUT (multi-book inbox) |
--continue-on-error |
With --batch, keep going after a failure (apply runs only) |
--json |
Success payload on stdout (scripting) |
-v, --verbose |
Path sanitization details on stderr |
Metadata overrides (single-book runs): --author, --title, --year, --series, --sequence, --narrator. With --batch, --series, --narrator, and --year may gap-fill empty fields per book; --author, --title, and --sequence are rejected.
Preview, copy, and move
abs-organize ~/Downloads/inbox/SomeBook --dry-run --library ~/Audiobooks
abs-organize ~/Downloads/inbox/SomeBook --library ~/Audiobooks
abs-organize ~/Downloads/inbox/SomeBook --library ~/Audiobooks --move
--dry-run uses the same validation as a real run and prints warnings to stderr, but does not create library paths or transfer files.
Batch inbox
If INPUT contains multiple book roots (e.g. several .m4b siblings), a plain run fails with a candidate list. Use --batch to organize all detected books:
abs-organize ~/Downloads/inbox --batch --library ~/Audiobooks --dry-run
abs-organize ~/Downloads/inbox --batch --library ~/Audiobooks --move
Dry-run always reports every book. On apply, batch stops at the first failure unless --continue-on-error is set.
Discovery (summary): each .m4b/.m4a sibling is its own book; .mp3/.flac/.ogg siblings in one folder are one book; Disc/CD/Disk subfolders roll up to one book at the parent.
Metadata and guessing
Tags are read with Mutagen:
| Folder segment | Tags |
|---|---|
| Author | albumartist or artist |
| Title folder | album or title (+ optional subtitle via config) |
| Series | grouping; sequence/year/narrator from tags, movement atoms (.m4b/.m4a), or OPF when present |
When album or title ends with a trailing narrator clause — (read by …), (narrated by …), or (performed by …) (or the same phrases in square brackets) — that clause is removed from the title folder name. The extracted name becomes narrator only if the composer tag is empty; if composer is set, it wins for the {Narrator} segment and the suffix is still stripped from the title.
Missing author or title tags exit with an error unless --allow-guess is set. Guesses use patterns such as Author - Title or Author - Title (YYYY) on the book folder or file stem; stderr marks them (confidence: low). CLI overrides always win.
Example (series layout):
{library}/Terry Goodkind/Sword of Truth/Vol 1 - 1994 - Wizards First Rule {Sam Tsoutsouvas}/book.m4b
Sidecars (desc.txt, reader.txt, cover images) are copied when present.
Configuration
File: ~/.config/abs-organize/config.toml
include_subtitle_in_folder = false
[libraries.default]
path = "/Users/you/Audiobooks"
[libraries.fiction]
path = "/Users/you/Audiobooks/Fiction"
[libraries.default]is required when you omit--library.include_subtitle_in_folder— append- {subtitle}to the title folder name.
Library path precedence
| Priority | Source |
|---|---|
| 1 | --library PATH |
| 2 | ABS_ORGANIZE_LIBRARY (only when --profile is omitted) |
| 3 | [libraries.{profile}].path when --profile NAME is set |
| 4 | [libraries.default].path |
Scripting (--json)
On success, stdout is JSON; errors stay on stderr (plain text). Warnings are in the JSON payload, not duplicated on stderr.
Single book:
{
"destination": "/Users/you/Audiobooks/Jane Author/Book Title/",
"files": ["book.mp3"],
"warnings": []
}
Batch:
{
"books": [
{
"source": "/inbox/Book A/",
"ok": true,
"destination": "/Audiobooks/Author/Title/",
"files": ["book.m4b"],
"warnings": []
}
],
"summary": { "ok": 1, "failed": 0 }
}
Unknown top-level keys may be added later; ignore fields you do not need.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | User or metadata error (missing tags, invalid paths, config/profile errors) |
| 2 | I/O error (copy, move, or filesystem failure) |
Batch: 0 only if every book succeeded; partial failure uses 1 or 2 if any book hit I/O errors.
Development
Release policy: see docs/RELEASE.md.
CI: GitHub Actions runs pytest on every push and pull request (.github/workflows/ci.yml). Require the CI status check to pass before merging to main (Settings → Branches → Branch protection rules).
| Path | Role |
|---|---|
src/abs_organize/cli.py |
Argument parsing and entry point |
src/abs_organize/organize.py |
Single-book copy/move pipeline |
src/abs_organize/batch.py |
Multi-book inbox orchestration |
src/abs_organize/discovery.py |
Book-root detection |
src/abs_organize/metadata.py |
Tag read, validation, overrides |
src/abs_organize/naming.py |
ABS-style path segments |
tests/ |
Pytest suite (test_data/ for fixtures) |
pip install -e ".[dev]"
pytest
abs-organize --help
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file abs_organize-0.1.1.tar.gz.
File metadata
- Download URL: abs_organize-0.1.1.tar.gz
- Upload date:
- Size: 40.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
13c1754852b63b40e0e6d9792ed8449d084c4227ca1c99433e9752a615800d6f
|
|
| MD5 |
cd208b0a4ac7e2d464da607b2769befb
|
|
| BLAKE2b-256 |
ec608d19d9bbfb467985729836dc23f261ec2c6410f75ba9c1054312ca35c1a5
|
Provenance
The following attestation bundles were made for abs_organize-0.1.1.tar.gz:
Publisher:
release.yml on trevordavies095/abs-organize
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
abs_organize-0.1.1.tar.gz -
Subject digest:
13c1754852b63b40e0e6d9792ed8449d084c4227ca1c99433e9752a615800d6f - Sigstore transparency entry: 1625447374
- Sigstore integration time:
-
Permalink:
trevordavies095/abs-organize@c0cc8b83fcba0e631e77b4a5fceb5722bd7b7d92 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/trevordavies095
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c0cc8b83fcba0e631e77b4a5fceb5722bd7b7d92 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file abs_organize-0.1.1-py3-none-any.whl.
File metadata
- Download URL: abs_organize-0.1.1-py3-none-any.whl
- Upload date:
- Size: 28.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f3c7d470afbb9a0c60970480e91b908fc1839b150554db3d6f14b043815b6aa2
|
|
| MD5 |
f991f5f30b319b30a53f7dbd16984ca4
|
|
| BLAKE2b-256 |
34d9e3a85b2b6d887d706346da8d40d54ab0a12a579d472423ebfaeb29dd573f
|
Provenance
The following attestation bundles were made for abs_organize-0.1.1-py3-none-any.whl:
Publisher:
release.yml on trevordavies095/abs-organize
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
abs_organize-0.1.1-py3-none-any.whl -
Subject digest:
f3c7d470afbb9a0c60970480e91b908fc1839b150554db3d6f14b043815b6aa2 - Sigstore transparency entry: 1625447406
- Sigstore integration time:
-
Permalink:
trevordavies095/abs-organize@c0cc8b83fcba0e631e77b4a5fceb5722bd7b7d92 -
Branch / Tag:
refs/heads/master - Owner: https://github.com/trevordavies095
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@c0cc8b83fcba0e631e77b4a5fceb5722bd7b7d92 -
Trigger Event:
workflow_dispatch
-
Statement type: