Imported from juanra/audiobook-forge (
AGENTS.md). Install upstream withnpx skills add juanra/audiobook-forge. Copyright stays with the author.
AGENTS.md - Audiobook Forge (Rust)
Project Overview
Audiobook Forge is a blazing-fast CLI tool for converting audiobook directories to M4B format with chapters and metadata. This is a complete Rust rewrite of the original Python version, delivering 3-4x faster performance with true multi-core utilization.
Key Capabilities:
- Parallel processing of multiple audiobooks
- Smart quality detection and preservation
- Chapter generation from multiple sources (files, CUE sheets, auto-detection)
- Metadata extraction and enhancement (ID3, M4A tags)
- Cover art detection and embedding
- Batch operations on entire libraries
- Error recovery with automatic retry
- Hardware acceleration support (Apple Silicon aac_at encoder)
Project Status: All 6 development phases complete. See docs/development/phases/ for detailed implementation history.
Build & Test Commands
Building
# Development build
cargo build
# Release build (optimized)
cargo build --release
# Binary location
./target/debug/audiobook-forge # debug
./target/release/audiobook-forge # release
Testing
# Run all tests
cargo test
# Run library tests only
cargo test --lib
# Run with output
cargo test -- --nocapture
# Run specific test
cargo test test_name
Running
# Check dependencies
cargo run -- check
# Process audiobooks
cargo run -- build --root /path/to/audiobooks --parallel 4
# Organize library
cargo run -- organize --root /path/to/audiobooks
# Config management
cargo run -- config init
cargo run -- config show
cargo run -- config validate
Architecture
Module Structure
src/
├── main.rs # CLI entry point
├── lib.rs # Library root
├── models/ # Core data structures
│ ├── book.rs # BookFolder, BookCase (A/B/C/D classification)
│ ├── track.rs # Audio track representation
│ ├── quality.rs # QualityProfile (bitrate, codec, etc.)
│ ├── config.rs # Configuration structures
│ └── result.rs # ProcessingResult
├── utils/ # Utilities
│ ├── config.rs # ConfigManager (load/save YAML)
│ ├── validation.rs # DependencyChecker (FFmpeg, etc.)
│ └── sorting.rs # Natural sorting (track1, track2, track10)
├── cli/ # CLI interface
│ └── commands.rs # Clap command definitions
├── audio/ # Audio operations
│ ├── ffmpeg.rs # FFmpeg wrapper (probe, concat, transcode)
│ ├── metadata.rs # Metadata extraction (ID3, M4A)
│ ├── chapters.rs # Chapter generation
│ └── cue.rs # CUE file parsing
└── core/ # Core processing logic
├── processor.rs # Single book processing
├── parallel.rs # Parallel batch processing
├── organizer.rs # Library organization
└── progress.rs # Progress tracking (indicatif)
Key Concepts
BookCase Classification:
- Case A: Already M4B (skip)
- Case B: MP3 files only (requires conversion)
- Case C: M4A files only (can use fast concat)
- Case D: Mixed MP3/M4A (requires transcode to AAC)
Quality Profiles: Automatically detect best quality across tracks and preserve it. Support compatibility checking for concatenation.
Processing Modes:
- Copy mode: Ultra-fast concatenation without re-encoding (when compatible)
- Transcode mode: Convert to AAC when necessary
Code Style & Conventions
Rust Patterns
- Use
anyhow::Result<T>for application errors - Use
thiserrorfor custom error types - Prefer
?operator over explicit error handling - Use
.context()to add error context - Follow Rust naming conventions (snake_case for functions/variables, PascalCase for types)
Error Handling
// Good - provides context
let tracks = discover_tracks(&path)
.context(format!("Failed to discover tracks in {}", path.display()))?;
// Avoid - no context
let tracks = discover_tracks(&path)?;
Async/Await
- Use tokio runtime for async operations
- FFmpeg calls use
tokio::process::Command - Parallel processing uses
tokio::spawnwith semaphore for resource limits
Testing
- Unit tests in same file as implementation (
#[cfg(test)] mod tests) - Integration tests in
tests/directory - Use
tempfilefor temp directories in tests - Mock external dependencies when possible
Testing Guidelines
Running Tests
All tests should pass before committing:
cargo test --lib
cargo test --all
Writing Tests
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_something() {
// Arrange
let input = "value";
// Act
let result = function_under_test(input);
// Assert
assert_eq!(result, expected);
}
#[tokio::test]
async fn test_async_function() {
// For async tests
}
}
Test Coverage
- All public functions should have tests
- Edge cases should be covered (empty input, invalid data, etc.)
- Quality profile comparison logic is critical (heavily tested)
Dependencies
External Tools (Required)
The application requires these external tools to be installed:
# macOS
brew install ffmpeg atomicparsley gpac
# Ubuntu/Debian
apt install ffmpeg atomicparsley gpac
Validation: Use audiobook-forge check to verify installation.
Rust Crates
Core:
clap 4.5- CLI framework with derive APItokio 1.35- Async runtimeserde 1.0+serde_yaml 0.9- Config serializationanyhow 1.0+thiserror 1.0- Error handling
Audio/Metadata:
id3 1.13- MP3 metadata readingmp4ameta 0.11- M4B/M4A metadata reading
Utilities:
indicatif 0.17- Progress barswalkdir 2.4- Directory traversalnatord 1.0- Natural sortingtracing 0.1- Structured loggingwhich 6.0- Executable lookupdirs 5.0- Config directory pathschrono 0.4- Date/time handling
Testing:
tempfile 3.8- Temp files/directoriesassert_cmd 2.0- CLI testingpredicates 3.0- Test assertions
Phase History
The project was built in 6 phases. Full documentation available in docs/development/phases/:
- Phase 1 - Foundation (
01-foundation.md): Data models, CLI structure, config system - Phase 2 - Audio Operations (
02-audio-ops.md): FFmpeg integration, metadata extraction - Phase 3 - Parallel Processing (
03-parallel.md): Multi-core batch processing - Phase 4 - Progress & Polish (
04-progress.md): Progress bars, error recovery - Phase 5 - Organization (
05-organize.md): Library organization features - Phase 6 - Final Polish (
06-polish.md): Performance optimization, final testing
Current Status: All phases complete, 77 tests passing.
Common Tasks
Adding a New CLI Command
- Define command in
src/cli/commands.rsusing clap derive - Add handler function in
main.rs - Implement logic in appropriate module
- Add tests
- Update help text
Adding a New Audio Operation
- Add function to
src/audio/ffmpeg.rsor related module - Use
tokio::process::Commandfor FFmpeg calls - Parse output with regex if needed
- Add error handling with context
- Write unit tests with temp files
Modifying Configuration
- Update structs in
src/models/config.rs - Add serde defaults if needed
- Update
templates/config.yamltemplate - Update validation in
src/utils/config.rs - Test serialization/deserialization
Performance Optimization
- Profile with
cargo flamegraphor similar tools - Check parallel worker count (default: CPU cores / 2)
- Verify copy mode is used when possible (no transcoding)
- Monitor memory usage during batch operations
Development Environment
Recommended Tools
rust-analyzer- LSP for IDE integrationcargo-watch- Auto-rebuild on file changescargo-clippy- Lintingcargo-fmt- Code formatting
Configuration Location
- Config file:
~/.config/audiobook-forge/config.yaml - Logs: stdout/stderr (structured with tracing)
Platform Notes
- macOS: Apple Silicon encoder (
aac_at) is automatically used when available - Linux: Standard AAC encoder (
libfdk_aacoraac) - FFmpeg must be compiled with AAC support
Commit & PR Guidelines
Commit Messages
Follow conventional commits format:
<type>(<scope>): <description>
[optional body]
[optional footer]
Types: feat, fix, docs, test, refactor, perf, chore
Examples:
feat(audio): add CUE file parsing support
fix(metadata): handle missing album tag gracefully
docs(readme): update installation instructions
test(quality): add compatibility check tests
Before Committing
- Run tests:
cargo test --all - Run clippy:
cargo clippy -- -D warnings - Format code:
cargo fmt - Update CHANGELOG.md if needed
- Ensure binary builds:
cargo build --release
Security Considerations
- Path Traversal: Validate all user-provided paths
- Command Injection: Never interpolate user input into shell commands
- Use
Command::arg()instead of string interpolation - FFmpeg args are always passed as separate arguments
- Use
- Resource Limits: Semaphore limits concurrent FFmpeg processes
- Temporary Files: Always clean up temp files (use
tempfilecrate)
Troubleshooting
Common Issues
FFmpeg not found:
- Run
audiobook-forge check - Ensure FFmpeg is in PATH
- On macOS:
brew install ffmpeg
Tests failing:
- Check external dependencies are installed
- Verify FFmpeg version is recent enough
- Check temp directory permissions
Slow processing:
- Check parallel worker count:
--parallel N - Verify copy mode is being used (check logs)
- Profile with release build, not debug
Metadata extraction fails:
- Ensure
atomicparsleyandMP4Boxare installed - Check file permissions
- Verify audio files are not corrupted
Additional Resources
- Main documentation:
docs/ - Phase implementation details:
docs/development/phases/ - Specifications:
docs/specs/(for future requirements) - User guides:
docs/guides/ - Changelog:
CHANGELOG.md(in root)