---
name: mutagen
description: Read and write audio metadata using the mutagen Python library. Use when the user wants to read, edit, embed, or remove tags (title, artist, album, cover art, lyrics, etc.) in MP3, FLAC, MP4/M4A, OGG, and other audio files.
---

# Mutagen

API reference for mutagen — a Python library for reading and writing audio metadata (tags) across multiple formats.

No dependencies outside the Python standard library. Supports Python 3.10+ (CPython and PyPy).

Official docs: https://mutagen.readthedocs.io
Repository: https://github.com/quodlibet/mutagen

## Installation

```bash
pip install mutagen
```

## Supported Formats

| Format | Class | Tag System |
|--------|-------|------------|
| MP3 | `mutagen.mp3.MP3` / `EasyMP3` | ID3v2 |
| FLAC | `mutagen.flac.FLAC` | Vorbis Comments |
| MP4 / M4A | `mutagen.mp4.MP4` / `EasyMP4` | iTunes-style atoms |
| Ogg Vorbis | `mutagen.oggvorbis.OggVorbis` | Vorbis Comments |
| Ogg Opus | `mutagen.oggopus.OggOpus` | Vorbis Comments |
| Ogg FLAC | `mutagen.oggflac.OggFLAC` | Vorbis Comments |
| Ogg Speex | `mutagen.oggspeex.OggSpeex` | Vorbis Comments |
| Ogg Theora | `mutagen.oggtheora.OggTheora` | Vorbis Comments |
| ASF / WMA | `mutagen.asf.ASF` | ASF attributes |
| AIFF | `mutagen.aiff.AIFF` | ID3v2 |
| WavPack | `mutagen.wavpack.WavPack` | APEv2 |
| Musepack | `mutagen.musepack.Musepack` | APEv2 |
| Monkey's Audio | `mutagen.monkeysaudio.MonkeysAudio` | APEv2 |
| True Audio | `mutagen.trueaudio.TrueAudio` | ID3v2 / APEv2 |
| OptimFROG | `mutagen.optimfrog.OptimFROG` | APEv2 |

## Core API

### Auto-Detection with `mutagen.File()`

```python
import mutagen

audio = mutagen.File("song.mp3")  # auto-detects format
print(audio.info.length)          # duration in seconds
print(audio.tags)                 # tag object (format-specific)
```

`mutagen.File()` returns the appropriate `FileType` subclass, or `None` if unrecognized.

Pass `easy=True` to get simplified tag access (EasyID3/EasyMP4):

```python
audio = mutagen.File("song.mp3", easy=True)
audio["title"] = ["My Song"]
audio.save()
```

### FileType (Base Class)

All format classes inherit from `FileType` and share this interface:

| Attribute / Method | Description |
|--------------------|-------------|
| `.info` | `StreamInfo` object — `length`, `bitrate`, `sample_rate`, `channels` |
| `.tags` | Tag object (dict-like), or `None` if no tags |
| `.mime` | List of applicable MIME types |
| `.save()` | Write tags to file |
| `.delete()` | Remove all tags from file |
| `.add_tags()` | Create new empty tag object (raises error if tags exist) |
| `.pprint()` | Human-readable stream info and tags |

---

## ID3 Tags (MP3, AIFF, TrueAudio)

### Reading / Writing with Raw ID3

```python
from mutagen.mp3 import MP3
from mutagen.id3 import ID3, TIT2, TPE1, TALB, TRCK, TDRC, TCON, APIC, COMM, USLT

audio = MP3("song.mp3")

# Read
print(audio["TIT2"].text[0])   # title
print(audio["TPE1"].text[0])   # artist

# Write
audio["TIT2"] = TIT2(encoding=3, text=["My Title"])
audio["TPE1"] = TPE1(encoding=3, text=["My Artist"])
audio.save()
```

### Common ID3 Frames

| Frame | Class | Description | Constructor |
|-------|-------|-------------|-------------|
| `TIT2` | TextFrame | Title | `TIT2(encoding=3, text=["..."])` |
| `TPE1` | TextFrame | Artist / Performer | `TPE1(encoding=3, text=["..."])` |
| `TPE2` | TextFrame | Album Artist | `TPE2(encoding=3, text=["..."])` |
| `TALB` | TextFrame | Album | `TALB(encoding=3, text=["..."])` |
| `TRCK` | NumericPartTextFrame | Track number (`"N/Total"`) | `TRCK(encoding=3, text=["1/12"])` |
| `TPOS` | NumericPartTextFrame | Disc number (`"N/Total"`) | `TPOS(encoding=3, text=["1/2"])` |
| `TDRC` | TimeStampTextFrame | Recording date | `TDRC(encoding=3, text=["2024"])` |
| `TCON` | TextFrame | Genre | `TCON(encoding=3, text=["Rock"])` |
| `TCOM` | TextFrame | Composer | `TCOM(encoding=3, text=["..."])` |
| `TBPM` | NumericTextFrame | BPM | `TBPM(encoding=3, text=["120"])` |
| `COMM` | TextFrame | Comment | `COMM(encoding=3, lang="eng", desc="", text=["..."])` |
| `USLT` | TextFrame | Lyrics | `USLT(encoding=3, lang="eng", desc="", text="...")` |
| `APIC` | Frame | Attached picture | `APIC(encoding=3, mime="image/jpeg", type=3, desc="", data=bytes)` |

### Encoding Values

| Value | Encoding |
|-------|----------|
| `0` | Latin-1 |
| `1` | UTF-16 |
| `2` | UTF-16BE |
| `3` | UTF-8 (recommended) |

### APIC Picture Types

| Value | Meaning |
|-------|---------|
| `0` | Other |
| `3` | Cover (front) |
| `4` | Cover (back) |
| `6` | Media (e.g. label side of CD) |

### ID3 Methods

| Method | Description |
|--------|-------------|
| `.add(frame)` | Add a frame (replaces matching frame) |
| `.getall(key)` | Get all frames matching key prefix |
| `.delall(key)` | Delete all frames matching key prefix |
| `.update_to_v23()` | Convert tags to ID3v2.3 (call before saving as v2.3) |
| `.update_to_v24()` | Convert tags to ID3v2.4 |
| `.save(v2_version=4)` | Save; set `v2_version=3` for ID3v2.3 |

### EasyID3 (Simplified Interface)

```python
from mutagen.easyid3 import EasyID3

audio = EasyID3("song.mp3")
audio["title"] = ["My Title"]
audio["artist"] = ["My Artist"]
audio["album"] = ["My Album"]
audio["tracknumber"] = ["1/12"]
audio["date"] = ["2024"]
audio["genre"] = ["Rock"]
audio.save()
```

Available EasyID3 keys: `title`, `artist`, `albumartist`, `album`, `tracknumber`, `discnumber`, `date`, `genre`, `composer`, `bpm`, `length`, `organization`, `website`, and more.

---

## MP3 Stream Info

```python
from mutagen.mp3 import MP3

audio = MP3("song.mp3")
info = audio.info
```

| Attribute | Type | Description |
|-----------|------|-------------|
| `info.length` | float | Duration in seconds |
| `info.bitrate` | int | Bits per second |
| `info.sample_rate` | int | Sampling frequency (Hz) |
| `info.channels` | int | Number of channels |
| `info.bitrate_mode` | BitrateMode | `UNKNOWN`, `CBR`, `VBR`, `ABR` |
| `info.encoder_info` | str | Encoder name/version |
| `info.track_gain` | float\|None | ReplayGain track gain |
| `info.track_peak` | float\|None | ReplayGain track peak |
| `info.album_gain` | float\|None | ReplayGain album gain |

---

## FLAC

```python
from mutagen.flac import FLAC

audio = FLAC("song.flac")
```

FLAC uses Vorbis Comments — tags are simple string key-value pairs (case-insensitive keys, multiple values per key).

### Reading / Writing Tags

```python
audio["title"] = ["My Title"]
audio["artist"] = ["My Artist"]
audio["album"] = ["My Album"]
audio["tracknumber"] = ["1"]
audio["date"] = ["2024"]
audio.save()
```

### Stream Info

| Attribute | Type | Description |
|-----------|------|-------------|
| `info.length` | float | Duration in seconds |
| `info.bitrate` | int | Bits per second |
| `info.sample_rate` | int | Sampling frequency (Hz) |
| `info.channels` | int | Number of channels |
| `info.bits_per_sample` | int | Bit depth |
| `info.total_samples` | int | Total number of samples |

### Embedded Pictures

```python
from mutagen.flac import FLAC, Picture

audio = FLAC("song.flac")

# Add picture
pic = Picture()
with open("cover.jpg", "rb") as f:
    pic.data = f.read()
pic.type = 3          # front cover
pic.mime = "image/jpeg"
pic.width = 500
pic.height = 500
pic.depth = 24
audio.add_picture(pic)
audio.save()

# Read pictures
for pic in audio.pictures:
    print(pic.mime, pic.type, len(pic.data))

# Remove all pictures
audio.clear_pictures()
audio.save()
```

---

## MP4 / M4A

```python
from mutagen.mp4 import MP4

audio = MP4("song.m4a")
```

### Common Tag Keys

| Key | Description |
|-----|-------------|
| `"\xa9nam"` | Title |
| `"\xa9ART"` | Artist |
| `"\xa9alb"` | Album |
| `"aART"` | Album artist |
| `"\xa9wrt"` | Composer |
| `"\xa9gen"` | Genre |
| `"\xa9day"` | Year / Date |
| `"\xa9lyr"` | Lyrics |
| `"\xa9cmt"` | Comment |
| `"trkn"` | Track number — `[(track, total)]` |
| `"disk"` | Disc number — `[(disc, total)]` |
| `"tmpo"` | BPM — `[120]` |
| `"cpil"` | Compilation — `True`/`False` |
| `"pgap"` | Gapless playback — `True`/`False` |
| `"covr"` | Cover art — list of `MP4Cover` objects |

### Reading / Writing Tags

```python
audio["\xa9nam"] = ["My Title"]
audio["\xa9ART"] = ["My Artist"]
audio["trkn"] = [(1, 12)]
audio.save()
```

### Cover Art

```python
from mutagen.mp4 import MP4, MP4Cover

audio = MP4("song.m4a")

# Add cover
with open("cover.jpg", "rb") as f:
    cover = MP4Cover(f.read(), imageformat=MP4Cover.FORMAT_JPEG)
audio["covr"] = [cover]
audio.save()

# Read cover
for cover in audio["covr"]:
    print(cover.imageformat)  # FORMAT_JPEG or FORMAT_PNG
    # cover is bytes-like — write directly to file
```

### MP4 Cover Formats

| Constant | Value |
|----------|-------|
| `MP4Cover.FORMAT_JPEG` | JPEG |
| `MP4Cover.FORMAT_PNG` | PNG |

### Stream Info

| Attribute | Type | Description |
|-----------|------|-------------|
| `info.length` | float | Duration in seconds |
| `info.bitrate` | int | Bits per second |
| `info.sample_rate` | int | Sampling frequency (Hz) |
| `info.channels` | int | Number of channels |
| `info.bits_per_sample` | int | Bit depth |
| `info.codec` | str | Codec identifier (e.g. `"mp4a.40.2"`, `"alac"`) |
| `info.codec_description` | str | Human-readable codec name |

### EasyMP4

```python
from mutagen.easymp4 import EasyMP4

audio = EasyMP4("song.m4a")
audio["title"] = ["My Title"]
audio["artist"] = ["My Artist"]
audio.save()
```

---

## Ogg Vorbis

```python
from mutagen.oggvorbis import OggVorbis

audio = OggVorbis("song.ogg")
```

Uses Vorbis Comments — same string key-value interface as FLAC:

```python
audio["title"] = ["My Title"]
audio["artist"] = ["My Artist"]
audio.save()
```

### Stream Info

| Attribute | Type | Description |
|-----------|------|-------------|
| `info.length` | float | Duration in seconds |
| `info.bitrate` | int | Nominal bitrate (bits/s) |
| `info.sample_rate` | int | Sampling frequency (Hz) |
| `info.channels` | int | Number of channels |

---

## Ogg Opus

```python
from mutagen.oggopus import OggOpus

audio = OggOpus("song.opus")
audio["title"] = ["My Title"]
audio.save()
```

Same Vorbis Comments interface. Stream info includes `info.length`, `info.channels`.

---

## Common Recipes

### Read all tags (any format)

```python
import mutagen

audio = mutagen.File("song.mp3")
for key, value in audio.tags.items():
    print(f"{key}: {value}")
```

### Set title and artist (any format, easy mode)

```python
import mutagen

audio = mutagen.File("song.mp3", easy=True)
audio["title"] = ["My Title"]
audio["artist"] = ["My Artist"]
audio.save()
```

### Embed cover art in MP3

```python
from mutagen.mp3 import MP3
from mutagen.id3 import ID3, APIC

audio = MP3("song.mp3")
if audio.tags is None:
    audio.add_tags()

with open("cover.jpg", "rb") as f:
    audio.tags.add(APIC(
        encoding=3,
        mime="image/jpeg",
        type=3,  # front cover
        desc="Cover",
        data=f.read()
    ))
audio.save()
```

### Extract cover art from MP3

```python
from mutagen.mp3 import MP3

audio = MP3("song.mp3")
for tag in audio.tags.getall("APIC"):
    with open("extracted_cover.jpg", "wb") as f:
        f.write(tag.data)
```

### Embed cover art in FLAC

```python
from mutagen.flac import FLAC, Picture

audio = FLAC("song.flac")
pic = Picture()
with open("cover.jpg", "rb") as f:
    pic.data = f.read()
pic.type = 3
pic.mime = "image/jpeg"
pic.width = 500
pic.height = 500
pic.depth = 24
audio.add_picture(pic)
audio.save()
```

### Embed cover art in MP4/M4A

```python
from mutagen.mp4 import MP4, MP4Cover

audio = MP4("song.m4a")
with open("cover.jpg", "rb") as f:
    audio["covr"] = [MP4Cover(f.read(), imageformat=MP4Cover.FORMAT_JPEG)]
audio.save()
```

### Add lyrics to MP3

```python
from mutagen.mp3 import MP3
from mutagen.id3 import USLT

audio = MP3("song.mp3")
audio.tags.add(USLT(encoding=3, lang="eng", desc="", text="Lyrics here..."))
audio.save()
```

### Remove all tags

```python
import mutagen

audio = mutagen.File("song.mp3")
audio.delete()
audio.save()
```

### Copy tags between files

```python
from mutagen.easyid3 import EasyID3

src = EasyID3("source.mp3")
dst = EasyID3("dest.mp3")
for key in src:
    dst[key] = src[key]
dst.save()
```

### Batch-read metadata from directory

```python
import mutagen
from pathlib import Path

for path in Path(".").glob("*.mp3"):
    audio = mutagen.File(str(path), easy=True)
    if audio and audio.tags:
        title = audio.tags.get("title", ["Unknown"])[0]
        artist = audio.tags.get("artist", ["Unknown"])[0]
        print(f"{path.name}: {artist} - {title}")
```

### Save as ID3v2.3 (compatibility)

```python
from mutagen.mp3 import MP3

audio = MP3("song.mp3")
audio.tags.update_to_v23()
audio.save(v2_version=3)
```

---

## Source

- Repository: https://github.com/quodlibet/mutagen
- Documentation: https://mutagen.readthedocs.io/en/latest/
- PyPI: https://pypi.org/project/mutagen/
- API — Base: https://mutagen.readthedocs.io/en/latest/api/base.html
- API — ID3: https://mutagen.readthedocs.io/en/latest/api/id3.html
- API — ID3 Frames: https://mutagen.readthedocs.io/en/latest/api/id3_frames.html
- API — MP3: https://mutagen.readthedocs.io/en/latest/api/mp3.html
- API — MP4: https://mutagen.readthedocs.io/en/latest/api/mp4.html
- API — FLAC: https://mutagen.readthedocs.io/en/latest/api/flac.html
- API — Ogg Vorbis: https://mutagen.readthedocs.io/en/latest/api/oggvorbis.html
