Thanks for your interest in contributing. This guide covers everything you need to get started.
# Fork and clone
gh repo fork rodaddy/audiobook-pipeline --clone
cd audiobook-pipeline
# Set up config for local testing
cp config.env.example config.env
# Edit config.env -- point WORK_DIR, OUTPUT_DIR, etc. to local test paths
# Check dependencies
./install.sh
# Test with dry-run
bin/audiobook-convert --dry-run --verbose /path/to/test-audiobook/See the README for the full dependency list. Run ./install.sh to check and install everything.
bin/ CLI entry points (audiobook-convert, cron-scanner, queue-processor)
lib/ Shared libraries (sourced by stages, never executed directly)
stages/ Pipeline stages (01-09, each self-contained with stage_*() function)
config.env.example Configuration template (copy to config.env)
docs/development/ Architecture docs, phase plans, and research history
.github/ Issue templates, PR template, CI workflow
Each stage is a standalone bash script that:
- Sets
SCRIPT_DIRandSTAGEvariables - Sources its dependencies from
lib/ - Defines a
stage_<name>()function - Checks required env vars with
: "${VAR:?error}" - Does its work (with
run()wrapper for dry-run support) - Updates the manifest with
manifest_set_stageandmanifest_update - Returns 0 on success, 1 on failure
Non-critical operations (cover art, chapters, verification) degrade gracefully -- they log warnings but don't fail the stage.
- Always use
#!/usr/bin/env bash-- never#!/bin/bash - Always use
set -euo pipefailat the top of executable scripts - Always run
shellcheckbefore submitting (CI enforces this) - Quote all variables:
"$var"not$var - Use
[[ ]]for conditionals, not[ ] - Conditional execution over if/else when simple:
[[ -n "$var" ]] && do_thing - Functions use lowercase with underscores:
fetch_audible_book,build_plex_path - Library-internal functions start with underscore:
_audnexus_cache_valid
Use the functions from lib/core.sh:
log_debug "Detailed info for --verbose mode"
log_info "Normal progress messages"
log_warn "Something went wrong but we can continue"
log_error "Something failed -- may need intervention"
die "Fatal error -- abort the pipeline"- Use
jqfor all JSON parsing -- never inline Python - Validate responses with
jq emptyorjq -ebefore caching - Use
// emptyfor optional fields to avoid null output
feat/description-- new featuresfix/description-- bug fixesdocs/description-- documentation changeschore/description-- maintenance, CI, dependencies
Use conventional commit format:
feat: add German Audible region support
fix: handle missing series position in metadata
docs: add troubleshooting section for region mismatch
chore: update shellcheck CI to v0.10
Keep the first line under 72 characters. Add a body for complex changes explaining the "why."
lib/audible.sh-- add the field tonormalize_audible_json()jq filterlib/metadata.sh-- extract the field intag_m4b()and add the tone flagREADME.md-- add a row to the Metadata Fields tableconfig.env.example-- add config var if the field is configurable
The field should be conditional: [[ -n "$field" ]] && tone_args+=("--meta-flag" "$field") so it works with both Audible and Audnexus sources.
- Create
stages/NN-name.shfollowing the existing pattern - Add the stage to
STAGE_MAPandSTAGE_ORDERinbin/audiobook-convert - Add any new library functions in
lib/name.sh - Source the library in
bin/audiobook-convert - Update manifest schema if the stage produces new state
There's no automated test suite yet (contributions welcome). For now:
# Lint all shell scripts
shellcheck -x -e SC1091,SC2034 lib/*.sh stages/*.sh bin/*.sh bin/audiobook-convert install.sh
# Dry-run a conversion
bin/audiobook-convert --dry-run --verbose /path/to/mp3-directory/
# Dry-run an enrichment
bin/audiobook-convert --dry-run --verbose /path/to/existing.m4b
# Test metadata-only mode
bin/audiobook-convert --mode metadata --dry-run --verbose /path/to/book.m4b
# Verify metadata was written
tone dump /path/to/tagged.m4b --format json- Fork the repo and create a feature branch from
main - Make your changes
- Run
shellcheckon all modified files - Test with
--dry-run --verboseagainst a sample audiobook - Submit a PR against
main-- the PR template will guide you - Address any review feedback
PRs that add new config options must also update config.env.example and the README.
Open a Discussion for questions, ideas, or anything that isn't a bug or feature request.