Watch a source folder and automatically transcode audio files to multiple formats
Project description
A containerized service that watches a source folder and automatically transcodes audio files to multiple formats simultaneously.
Perfect for maintaining a music library in multiple formats -- lossless for archival, lossy for portable devices -- without lifting a finger. Drop a FLAC into your source folder and get ALAC, MP3, AAC, and Opus copies instantly.
Table of Contents
- Features
- Quick Start
- Configuration
- How It Works
- Performance
- Verification Tool
- Development
- License
- Contributing
Features
- Real-time file watching -- Detects new, modified, renamed, or deleted files via watchdog
- Recursive directory support -- Mirrors source folder hierarchy (Artist/Album) in all outputs
- Multiple simultaneous outputs -- Transcode to any number of formats in a single pass
- Six codecs -- ALAC, AAC, MP3, Opus, FLAC, WAV with configurable bitrates
- Synced lyrics -- Auto-fetches
.lrclyrics with Whisper speech-to-text fallback - Artwork preservation -- Optionally embeds cover art in output files
- Atomic writes -- No partial or corrupted files on failure
- Orphan cleanup -- Automatically removes outputs that no longer have a source
- Safety guards -- Prevents accidental mass deletion if folders appear empty
- Unicode safe -- Properly handles special characters in filenames
- Docker-first -- Ships as a lightweight container built on Python 3.14-slim + FFmpeg
Quick Start
Docker Compose (Recommended)
1. Create a config.yaml file:
source:
path: /music/flac
outputs:
- name: alac
codec: alac
path: /music/alac
- name: mp3-256
codec: mp3
bitrate: 256k
path: /music/mp3
- name: aac-256
codec: aac
bitrate: 256k
path: /music/aac
2. Create a docker-compose.yml:
services:
audio-transcoder:
image: drumsergio/audio-transcoder:0.5.0
container_name: audio_transcoder
environment:
- TZ=Europe/Madrid
- CONFIG_FILE=/app/config.yaml
volumes:
- ./config.yaml:/app/config.yaml:ro
- /path/to/flac:/music/flac:ro
- /path/to/alac:/music/alac
- /path/to/mp3:/music/mp3
- /path/to/aac:/music/aac
restart: unless-stopped
3. Start the service:
docker compose up -d
Docker CLI
docker run -d \
--name audio_transcoder \
-e TZ=Europe/Madrid \
-e CONFIG_FILE=/app/config.yaml \
-v ./config.yaml:/app/config.yaml:ro \
-v /path/to/flac:/music/flac:ro \
-v /path/to/mp3:/music/mp3 \
--restart unless-stopped \
drumsergio/audio-transcoder:0.5.0
Configuration
Configuration is provided via a YAML file. Set the CONFIG_FILE environment variable to its path inside the container.
Full Configuration Example
# Source folder containing original audio files
source:
path: /music/flac
# Output destinations -- define as many as you need
outputs:
# Lossless ALAC for Apple devices
- name: alac
codec: alac
path: /music/alac
include_artwork: true
# High-quality MP3 for broad compatibility
- name: mp3-320
codec: mp3
bitrate: 320k
path: /music/mp3-320
include_artwork: true
# Balanced MP3 for portable devices
- name: mp3-192
codec: mp3
bitrate: 192k
path: /music/mp3-192
include_artwork: true
# AAC for modern devices
- name: aac-256
codec: aac
bitrate: 256k
path: /music/aac
include_artwork: true
# Opus for streaming (best quality-to-size ratio)
- name: opus-128
codec: opus
bitrate: 128k
path: /music/opus
# Optional settings
settings:
# Delete all outputs and re-encode on startup
force_reencode: false
# Maximum time to wait for a file to become stable (seconds)
stability_timeout: 60
# Minimum time a file must be unchanged before processing (seconds)
min_stable_seconds: 1.0
Supported Codecs
| Codec | Extension | Bitrate | Artwork | Description |
|---|---|---|---|---|
alac |
.m4a |
N/A | Yes | Lossless, Apple compatible |
aac |
.m4a |
64k--320k | Yes | Lossy, excellent quality |
mp3 |
.mp3 |
64k--320k | Yes | Lossy, universal support |
opus |
.opus |
32k--256k | No | Lossy, best quality/size |
flac |
.flac |
N/A | Yes | Lossless, open format |
wav |
.wav |
N/A | No | Lossless, uncompressed |
JSON Configuration
You can alternatively provide configuration as a JSON string via the CONFIG_JSON environment variable:
environment:
- CONFIG_JSON={"source":{"path":"/music/flac"},"outputs":[{"name":"mp3","codec":"mp3","bitrate":"256k","path":"/music/mp3"}]}
How It Works
- Initial sync -- On startup, scans the source folder and encodes any missing files to all configured outputs.
- Watch mode -- Continuously monitors the source folder for changes:
- New files are encoded to all configured outputs
- Modified files are re-encoded to all outputs
- Renamed files trigger deletion of old outputs and creation of new ones
- Deleted files have their corresponding outputs removed
- Orphan cleanup -- Removes output files that no longer have a matching source.
- Lyrics sync -- Fetches synced
.lrclyrics from online databases; falls back to Whisper transcription when no lyrics are found.
Recursive Directory Support
Source folder hierarchy is automatically mirrored in all outputs. Both flat and nested structures work out of the box -- no configuration needed.
Source: Output (MP3):
/music/flac/ /music/mp3/
├── Artist A/ ├── Artist A/
│ ├── Album 1/ │ ├── Album 1/
│ │ ├── 01 - Track.flac │ │ ├── 01 - Track.mp3
│ │ └── 02 - Track.flac │ │ └── 02 - Track.mp3
│ └── Album 2/ │ └── Album 2/
│ └── 01 - Song.flac │ └── 01 - Song.mp3
└── Artist B/ └── Artist B/
└── Live Album/ └── Live Album/
└── 01 - Intro.flac └── 01 - Intro.mp3
When source files or directories are deleted, the corresponding outputs and empty directories are cleaned up automatically.
Safety Guards
The service includes multiple guards to prevent data loss:
- If the source folder appears empty, no deletions are performed
- If any output folder appears empty, no deletions are performed
- All writes are atomic -- encoding happens to a temporary file that is moved into place only on success
Performance
- Parallel processing -- Multiple output formats are encoded concurrently
- Incremental sync -- Only missing or changed files are processed; unchanged files are skipped
- Stability detection -- Files are not processed until they have been stable on disk for a configurable period, avoiding partial reads during large copies or network transfers
- Low idle footprint -- Uses inotify/FSEvents-based watching with minimal CPU usage when idle
Verification Tool
A built-in verification tool checks that all outputs are in sync with the source:
# Basic sync check
docker exec audio_transcoder python /app/tools/verify_sync.py --config /app/config.yaml
# Thorough check including duration comparison
docker exec audio_transcoder python /app/tools/verify_sync.py --config /app/config.yaml --check-duration -v
Development
Requirements
- Docker (recommended), or Python 3.14+ with FFmpeg installed
- Hatch build system
Running Tests
python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
# Run tests with coverage
pytest
Building the Docker Image
docker build -t audio-transcoder:dev .
Related Music Tools
| Project | Description |
|---|---|
| slskd-transform | Bulk upgrade your music library from lossy to lossless via Soulseek |
| telegram-slskd-local-bot | Automated music discovery and download via Telegram |
| jellyfin-encoder | Automatic 720p HEVC/AV1 transcoding for Jellyfin |
License
This project is licensed under the GNU General Public License v3.0 -- see the LICENSE file for details.
Contributing
Contributions are welcome. Please open an issue to discuss significant changes before submitting a pull request.
- Fork the repository
- Create a feature branch (
git checkout -b feat/amazing-feature) - Run tests (
pytest) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feat/amazing-feature) - Open a Pull Request
Project details
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 audio_transcode_watcher-0.5.1.tar.gz.
File metadata
- Download URL: audio_transcode_watcher-0.5.1.tar.gz
- Upload date:
- Size: 94.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e2b5058c32f1becca49e6534fe4b948563fabc63ff358d679f61e34fc2efbb3a
|
|
| MD5 |
0b1d82c3eece93124dceafa040aa226c
|
|
| BLAKE2b-256 |
18b5074edca9b0121ef949a71550b66a4bc46b099b2079d5653107a0f144ef45
|
File details
Details for the file audio_transcode_watcher-0.5.1-py3-none-any.whl.
File metadata
- Download URL: audio_transcode_watcher-0.5.1-py3-none-any.whl
- Upload date:
- Size: 33.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cac58edfc4cf19069b6f1c8607074a003a59a905b853128d0f20b0c2e06394a8
|
|
| MD5 |
73be14df374ba40be1a310da5b047024
|
|
| BLAKE2b-256 |
228f7231d9c4444c4a6d075ca7bdde6ba391c6175aed517ea705764d12603748
|