# 🎵 Audio Splitter

[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
[![FFmpeg](https://img.shields.io/badge/FFmpeg-required-green.svg)](https://ffmpeg.org/)
[![License: GPLv3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
[![Docker](https://img.shields.io/badge/Docker-ready-blue.svg)](https://www.docker.com/)

**Split audio files into tracks using a flexible tracklist format.**

> A powerful command‑line tool that reads a tracklist (with timestamps) and splits an audio file into individual tracks. Supports multiple audio/video containers, metadata writing, and fully customisable input/output formats.

---

## ✨ Features

- **Flexible tracklist parsing** – Define your own format with placeholders (`%ts`, `%tn`, `%an`, `%al`, `%date`, `%ext`)
- **Timestamp support** – Handle both `start-only` (`00:00`) and `start:end` (`00:00-01:30`) timestamps
- **Multiple stream support** – Preserve audio, video, and subtitle streams (or drop them selectively)
- **Metadata writing** – Write `title`, `artist`, `album`, `date`, `track`, and `comment` to output files
- **Custom output filenames** – Define your own naming template using placeholders (`%an-%tn.%ext`)
- **Character replacement** – Replace problematic filename characters with a custom character
- **Dry‑run mode** – Preview parsed tracks without splitting
- **Skip existing files** – Avoid overwriting already‑extracted tracks
- **Delete original** – Optionally remove the input file after successful splitting
- **Docker image** – Run without installing dependencies; simple one‑line commands

---

## 📦 Installation

### Prerequisites

- **Python 3.8 or higher**
- **FFmpeg** – required for audio processing

### Install FFmpeg

| OS                   | Command                                                      |
| -------------------- | ------------------------------------------------------------ |
| **Ubuntu/Debian**    | `sudo apt install ffmpeg`                                    |
| **macOS (Homebrew)** | `brew install ffmpeg`                                        |
| **Windows**          | Download from [ffmpeg.org](https://ffmpeg.org/download.html) |

### Install Audio Splitter

Clone the repository and install:

```bash
git clone https://git.vmn.su/max/audio_splitter.git
cd audio_splitter
pip install .
```

Now the `audio_splitter` command is available globally:

```bash
audio_splitter input.mp3 tracks.txt
```

> **Tip:** For development, install in editable mode: `pip install -e .`

---

## 🚀 Quick Start

### 1. Prepare a tracklist file

Create a `tracks.txt` file with one track per line:

```
00:00 Intro
01:30 Song One - Artist A
04:20-06:45 Another Song - Artist B
08:10 Finale - Artist C
```

- `00:00` – start‑only timestamp (track ends at next track's start or end of file)
- `04:20-06:45` – explicit start and end timestamps

### 2. Run the splitter

```bash
audio_splitter my_album.mp3 tracks.txt
```

Output:

```
Found 4 tracks.
Detected streams: audio=True, video=False, subs=False
Audio codec: mp3
Output container: mp3
Extracting track 1: Intro (00:00:00 - 00:01:30)
  -> Saved to: my_album_splits/Intro.mp3
Extracting track 2: Song One (00:01:30 - 00:04:20)
  -> Saved to: my_album_splits/Song One - Artist A.mp3
...
Done!
```

---

## ⚙️ Command‑Line Options

### Basic Options

| Option                 | Description                                                   |
| ---------------------- | ------------------------------------------------------------- |
| `input_file`           | Input audio/video file                                        |
| `tracklist_file`       | Tracklist file                                                |
| `-o, --output-dir DIR` | Output directory (default: `<input>_splits`)                  |
| `--format FORMAT`      | Output container format (mp3, m4a, mkv, mp4, ogg, opus, etc.) |
| `--transcode-to CODEC` | Re‑encode audio to CODEC (e.g., libmp3lame, aac, libopus)     |
| `--drop-video`         | Remove video streams                                          |
| `--drop-subs`          | Remove subtitle streams                                       |
| `--dry-run`            | Preview parsed tracklist without splitting                    |

### Filename Options

| Option                       | Description                                                     |
| ---------------------------- | --------------------------------------------------------------- |
| `--number-tracks`            | Prepend track number (`01 - `) to filenames                     |
| `--output-template TEMPLATE` | Custom filename template (default: `%an-%tn.%ext`)              |
| `--replace-bad-chars`        | Replace problematic characters in filenames                     |
| `--replacement-char CHAR`    | Replacement character (default: `_`)                            |
| `--bad-chars CHARS`          | Characters to replace (default includes space and single quote) |

### Metadata Options

| Option                    | Description                                     |
| ------------------------- | ----------------------------------------------- |
| `--album ALBUM`           | Set album name (overrides parsed `%al`)         |
| `--comment COMMENT`       | Set comment text                                |
| `--no-comment`            | Ignore comment entirely                         |
| `--comment-stream INDEX`  | Select comment from a specific stream (0‑based) |
| `--merge-comments`        | Merge all comments from all streams             |
| `--comment-separator SEP` | Separator for merged comments (default: `; `)   |

### Tracklist Format Options

| Option                      | Description                                        |
| --------------------------- | -------------------------------------------------- |
| `--tracklist-format FORMAT` | Custom tracklist format (default: `%ts %tn - %an`) |
| `--skip-existing`           | Skip extraction if output file already exists      |

### Other Options

| Option              | Description                                               |
| ------------------- | --------------------------------------------------------- |
| `--delete-original` | Delete the original input file after successful splitting |

---

## 📝 Placeholders Reference

### Tracklist Format Placeholders (`--tracklist-format`)

| Placeholder | Meaning                                             |
| ----------- | --------------------------------------------------- |
| `%ts`       | **Timestamp** – required (`00:00` or `00:00-01:30`) |
| `%tn`       | Track name                                          |
| `%an`       | Author/artist                                       |
| `%al`       | Album                                               |
| `%date`     | Date/year                                           |
| `%ext`      | File extension                                      |

**Default:** `%ts %tn - %an`

### Output Template Placeholders (`--output-template`)

| Placeholder | Meaning                                |
| ----------- | -------------------------------------- |
| `%tn`       | Track name                             |
| `%an`       | Author/artist                          |
| `%al`       | Album                                  |
| `%date`     | Date/year                              |
| `%ext`      | File extension (without leading dot)   |
| `%num`      | Track number (zero‑padded, e.g., `01`) |

**Default:** `%an-%tn.%ext`

---

## 💡 Examples

### Custom tracklist format

If your tracklist uses `artist - title [time]`:

```bash
audio_splitter input.flac tracks.txt \
  --tracklist-format "%an - %tn [%ts]"
```

### Custom output filenames

Name files as `01 - Artist - Song.mp3`:

```bash
audio_splitter input.flac tracks.txt \
  --output-template "%num - %an - %tn.%ext"
```

### Override album and comment

```bash
audio_splitter input.flac tracks.txt \
  --album "Greatest Hits" \
  --comment "Live recording"
```

### Merge multiple comments from input file

```bash
audio_splitter input.flac tracks.txt \
  --merge-comments \
  --comment-separator " | "
```

### Delete original file after splitting

```bash
audio_splitter input.flac tracks.txt --delete-original
```

### Dry‑run to preview parsing

```bash
audio_splitter input.flac tracks.txt --dry-run
```

Output:

```
Parsed tracklist:
------------------------------------------------------------
        ts |         tn |         an
------------------------------------------------------------
  1 |     00:00 |      Intro |
  2 |     01:30 |   Song One |  Artist A
  3 |  04:20-06:45 | Another Song |  Artist B
...
------------------------------------------------------------
Dry‑run complete. No files were created.
```

---

## 🐳 Docker

You can run `audio_splitter` in a Docker container without installing Python or FFmpeg on your host.

### Pull the Image (Optional)

```bash
docker pull yourusername/audio_splitter:latest
```

### Build the Image Locally

```bash
docker build -t audio_splitter .
```

### Usage

**You must mount your working directory to `/data` inside the container.**  
The container will automatically adjust permissions so that you can read input files and write output files.

Simply provide the arguments as you would to the `audio_splitter` command:

```bash
docker run --rm -v $(pwd):/data audio_splitter /data/input.mp3 /data/tracks.txt [OPTIONS]
```

#### Examples

**Basic split:**

```bash
docker run --rm -v $(pwd):/data audio_splitter /data/input.mp3 /data/tracks.txt
```

**With custom options:**

```bash
docker run --rm -v $(pwd):/data audio_splitter /data/input.mp3 /data/tracks.txt --album "Greatest Hits" --format mp3 --number-tracks
```

**Dry‑run:**

```bash
docker run --rm -v $(pwd):/data audio_splitter /data/input.mp3 /data/tracks.txt --dry-run
```

**Help:**

```bash
docker run --rm audio_splitter --help
```

### Output

All output files are written to the mounted directory on your host (under the default `input_splits/` subdirectory, or any custom `--output-dir` you specify).

### Permission Handling

The container automatically adjusts ownership of the mounted `/data` directory so that the container user can read and write files there. No `--user` or `:z` flags are required.

---

## 📂 Project Structure

```
audio_splitter/
├── __init__.py          # Package initialisation
├── constants.py         # Global constants (FORMAT_INFO, DEFAULT_BAD_CHARS)
├── utils.py             # Generic helpers (timestamps, string manipulation)
├── tracklist.py         # Tracklist parsing with custom formats
├── ffmpeg.py            # FFmpeg/FFprobe interactions and command building
├── timestamp.py         # Timestamp parsing and resolution
├── filename.py          # Output filename generation
├── metadata.py          # Metadata selection and building
├── formats.py           # Container format decision and validation
├── core.py              # Main orchestration logic
├── main.py              # Command‑line interface
├── docker-entrypoint.sh # Docker entrypoint script
├── Dockerfile           # Docker image definition
└── README.md            # This file
```

---

## 🤝 Contributing

Contributions are welcome! Please follow these steps:

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

For bug reports or feature requests, please [open an issue](https://git.vmn.su/max/audio_splitter/issues).

---

## 📄 License

Distributed under the GNU General Public License v3 (or later). See the [LICENSE](LICENSE) file for more details.

---

## 🙏 Acknowledgements

- [FFmpeg](https://ffmpeg.org/) – the powerhouse behind audio processing
- [Python](https://www.python.org/) – the language that makes it all possible
- [gosu](https://github.com/tianon/gosu) – privilege dropping for Docker

---

## 📬 Contact

**Maintainer:** Maxim Vershinin
**Project Link:** [https://git.vmn.su/max/audio_splitter](https://git.vmn.su/max/audio_splitter)
