Python library for handling audio metadata across multiple formats including MP3, FLAC, MP4, OGG and many others
—
This document covers MP3 audio format support and complete ID3v2 tag implementation in Mutagen. MP3 is one of the most widely supported formats with comprehensive metadata capabilities through ID3 tags.
# MP3 format classes
from mutagen.mp3 import MP3, EasyMP3, MPEGInfo, BitrateMode
from mutagen.mp3 import Open, delete
from mutagen.mp3 import STEREO, JOINTSTEREO, DUALCHANNEL, MONO
# ID3v2 tag classes and frames
from mutagen.id3 import ID3, ID3FileType, Frame, TextFrame, UrlFrame, BinaryFrame
from mutagen.id3 import Encoding, PictureType, ID3NoHeaderError, ID3UnsupportedVersionError
# Common ID3 frames
from mutagen.id3 import TIT2, TPE1, TALB, TRCK, TYER, TCON, APIC, COMM, GEOB
# Easy interface for ID3
from mutagen.easyid3 import EasyID3class MP3(ID3FileType):
"""MPEG audio file with ID3v2 tags.
Supports MPEG-1, MPEG-2, and MPEG-2.5 audio layers I, II, and III.
Provides access to ID3v2 tags and MPEG stream information.
Attributes:
info: MPEGInfo instance with stream details
tags: ID3 tag container (ID3 instance)
filename: Path to the MP3 file
"""
def __init__(self, filename: str) -> None:
"""Load MP3 file and parse ID3 tags and stream info.
Args:
filename: Path to MP3 file
Raises:
MutagenError: If file is not valid MP3 or corrupted
IOError: If file cannot be accessed
"""
def add_tags(self) -> None:
"""Add empty ID3v2.4 tag if no tags exist."""
def save(self, v1=1, v2_version=4, v23_sep='/', v2_padding=None, **kwargs) -> None:
"""Save ID3 tags to file.
Args:
v1: ID3v1 handling (0=none, 1=update, 2=create)
v2_version: ID3v2 version (3 or 4)
v23_sep: Text separator for ID3v2.3 multi-values
v2_padding: Padding strategy function or None for default
"""
class EasyMP3(MP3):
"""MP3 with EasyID3 interface for simplified tag access.
Uses dictionary-like tag names instead of ID3 frame names.
"""
# Function aliases
Open = MP3 # Alternative constructor name
def delete(filename: str) -> None:
"""Remove ID3 tags from MP3 file.
Args:
filename: Path to MP3 file
"""
# Usage examples
from mutagen.mp3 import MP3
# Load MP3 file
mp3_file = MP3("song.mp3")
# Access stream info
print(f"Duration: {mp3_file.info.length} seconds")
print(f"Bitrate: {mp3_file.info.bitrate} bps")
# Access ID3 tags
print(mp3_file["TIT2"]) # Title
print(mp3_file["TPE1"]) # Artist
# Modify tags
mp3_file["TIT2"] = "New Title"
mp3_file["TPE1"] = "New Artist"
mp3_file.save()
# Delete all tags
from mutagen.mp3 import delete
delete("song.mp3")class MPEGInfo:
"""MPEG audio stream information.
Contains technical details about the MP3 audio stream including
bitrate, sample rate, duration, and encoding parameters.
Attributes:
length: Duration in seconds (float)
bitrate: Bitrate in bits per second (int)
sample_rate: Sample rate in Hz (int)
channels: Number of audio channels (int)
layer: MPEG layer (1, 2, or 3)
version: MPEG version (1, 2, or 2.5)
mode: Channel mode (STEREO, JOINTSTEREO, DUALCHANNEL, MONO)
bitrate_mode: BitrateMode enum (CBR, VBR, ABR, or UNKNOWN)
encoder_info: Encoder identification string
encoder_settings: Encoder settings string
sketchy: True if stream has potential issues
"""
class BitrateMode:
"""MPEG bitrate mode enumeration."""
UNKNOWN = 0
CBR = 1 # Constant Bitrate
VBR = 2 # Variable Bitrate
ABR = 3 # Average Bitrate
# Channel mode constants
STEREO = 0
JOINTSTEREO = 1
DUALCHANNEL = 2
MONO = 3
# Usage examples
mp3_file = MP3("song.mp3")
info = mp3_file.info
print(f"MPEG Layer: {info.layer}")
print(f"Version: {info.version}")
print(f"Sample rate: {info.sample_rate} Hz")
print(f"Channels: {info.channels}")
print(f"Mode: {info.mode}")
print(f"Bitrate mode: {info.bitrate_mode}")
if hasattr(info, 'encoder_info'):
print(f"Encoder: {info.encoder_info}")class ID3:
"""ID3v2 tag container.
Dictionary-like container for ID3v2 frames. Supports ID3v2.2, 2.3, and 2.4.
Frame access uses 4-character ID3v2.4 frame names (e.g., TIT2, TPE1).
Attributes:
version: ID3v2 version tuple (major, minor)
unknown_frames: List of frames not recognized by Mutagen
"""
def __init__(self, filename: str = None) -> None:
"""Load ID3 tags from file.
Args:
filename: Path to file with ID3 tags (optional)
"""
def save(self, filename: str, v1=1, v2_version=4, v23_sep='/',
v2_padding=None, **kwargs) -> None:
"""Save ID3 tags to file.
Args:
filename: Target file path
v1: ID3v1 mode (0=none, 1=update existing, 2=always create)
v2_version: ID3v2 version to write (3 or 4)
v23_sep: Separator for multi-value frames in ID3v2.3
v2_padding: Padding function or None
"""
def delete(self, filename: str) -> None:
"""Remove ID3 tags from file."""
def update_to_v24(self) -> None:
"""Update frame names from older ID3 versions to v2.4."""
def update_to_v23(self) -> None:
"""Update frame names from v2.4 to v2.3 for compatibility."""
# Usage examples
from mutagen.id3 import ID3
# Load ID3 tags
tags = ID3("song.mp3")
# Check version
print(f"ID3v2.{tags.version[0]}.{tags.version[1]}")
# Frame access
title_frame = tags.get("TIT2")
if title_frame:
print(f"Title: {title_frame.text[0]}")
# Add frame
from mutagen.id3 import TIT2
tags["TIT2"] = TIT2(encoding=3, text="Song Title")
tags.save("song.mp3")class Frame:
"""Base class for all ID3v2 frames.
Each frame type has specific attributes and methods for handling
its particular data format and encoding requirements.
"""
class TextFrame(Frame):
"""Base class for text information frames.
Attributes:
encoding: Text encoding (0=latin1, 1=utf16, 2=utf16be, 3=utf8)
text: List of text strings
"""
class UrlFrame(Frame):
"""Base class for URL link frames.
Attributes:
url: URL string
"""
class BinaryFrame(Frame):
"""Base class for frames containing arbitrary binary data.
Attributes:
data: Raw bytes data
"""
# Common text frames
class TIT2(TextFrame):
"""Title/Song name/Content description frame."""
class TPE1(TextFrame):
"""Lead artist(s)/Lead performer(s)/Soloist(s)/Performing group frame."""
class TALB(TextFrame):
"""Album/Movie/Show title frame."""
class TRCK(TextFrame):
"""Track number/Position in set frame."""
class TYER(TextFrame):
"""Year frame (deprecated in ID3v2.4, use TDRC)."""
class TCON(TextFrame):
"""Content type (genre) frame."""
# Attached picture frame
class APIC(Frame):
"""Attached picture frame.
Attributes:
encoding: Text encoding for description
mime: MIME type (e.g., 'image/jpeg')
type: Picture type (PictureType enum)
desc: Picture description string
data: Image data bytes
"""
class PictureType:
"""Picture type enumeration for APIC frames."""
OTHER = 0
FILE_ICON = 1
OTHER_FILE_ICON = 2
COVER_FRONT = 3
COVER_BACK = 4
LEAFLET_PAGE = 5
MEDIA = 6
LEAD_ARTIST = 7
ARTIST = 8
CONDUCTOR = 9
BAND = 10
COMPOSER = 11
LYRICIST = 12
RECORDING_LOCATION = 13
DURING_RECORDING = 14
DURING_PERFORMANCE = 15
VIDEO_SCREEN_CAPTURE = 16
BRIGHT_COLORED_FISH = 17
ILLUSTRATION = 18
BAND_LOGOTYPE = 19
PUBLISHER_LOGOTYPE = 20
# Comment frame
class COMM(Frame):
"""Comments frame.
Attributes:
encoding: Text encoding
lang: 3-character language code
desc: Short description
text: List of comment strings
"""
# Additional important frame types
class TBPM(TextFrame):
"""Beats per minute frame."""
class TCOM(TextFrame):
"""Composer frame."""
class TCOP(TextFrame):
"""Copyright message frame."""
class TDAT(TextFrame):
"""Date frame (DDMM format)."""
class TENC(TextFrame):
"""Encoded by frame."""
class TKEY(TextFrame):
"""Initial key frame."""
class TLAN(TextFrame):
"""Language frame."""
class TLEN(TextFrame):
"""Length frame (in milliseconds)."""
class TPOS(TextFrame):
"""Part of set frame (disc number)."""
class TPUB(TextFrame):
"""Publisher frame."""
class TXXX(TextFrame):
"""User-defined text frame.
Attributes:
encoding: Text encoding
desc: Description string
text: List of text strings
"""
class WXXX(UrlFrame):
"""User-defined URL frame.
Attributes:
encoding: Text encoding
desc: Description string
url: URL string
"""
class GEOB(Frame):
"""General encapsulated object frame.
Attributes:
encoding: Text encoding
mime: MIME type
filename: Filename
desc: Description
data: Binary data
"""
class PRIV(Frame):
"""Private frame.
Attributes:
owner: Owner identifier string
data: Binary private data
"""
class USLT(Frame):
"""Unsynchronized lyrics/text transcription frame.
Attributes:
encoding: Text encoding
lang: 3-character language code
desc: Content descriptor
text: Lyrics text
"""
# Usage examples
from mutagen.id3 import TIT2, TPE1, APIC, PictureType
# Create text frames
title = TIT2(encoding=3, text=["Song Title"])
artist = TPE1(encoding=3, text=["Artist Name"])
# Create picture frame
with open("cover.jpg", "rb") as f:
cover_data = f.read()
cover = APIC(
encoding=3,
mime="image/jpeg",
type=PictureType.COVER_FRONT,
desc="Front Cover",
data=cover_data
)
# Add to MP3
mp3_file = MP3("song.mp3")
mp3_file["TIT2"] = title
mp3_file["TPE1"] = artist
mp3_file["APIC:"] = cover
mp3_file.save()class EasyID3(ID3):
"""Dictionary-like ID3 interface with normalized tag names.
Provides simple string-based tag names instead of ID3 frame names.
Automatically handles encoding and multi-value conversion.
Supported keys:
title, artist, album, date, tracknumber, discnumber, genre,
albumartist, composer, performer, lyricist, conductor, etc.
"""
# Available tag mappings
valid_keys = {
'title': 'TIT2',
'artist': 'TPE1',
'album': 'TALB',
'date': 'TDRC',
'tracknumber': 'TRCK',
'discnumber': 'TPOS',
'genre': 'TCON',
'albumartist': 'TPE2',
# ... many more
}
# Usage examples
from mutagen.easyid3 import EasyID3
# Load with easy interface
easy_tags = EasyID3("song.mp3")
# Simple tag access
easy_tags["title"] = ["Song Title"]
easy_tags["artist"] = ["Artist Name"]
easy_tags["album"] = ["Album Name"]
easy_tags["date"] = ["2023"]
easy_tags["tracknumber"] = ["1/12"]
# Multi-value tags
easy_tags["genre"] = ["Rock", "Alternative"]
easy_tags["artist"] = ["Primary Artist", "Featured Artist"]
# Save changes
easy_tags.save()
# Read tags
print(f"Title: {easy_tags['title'][0]}")
print(f"Artist: {', '.join(easy_tags['artist'])}")
# List available keys
print("Available keys:", list(EasyID3.valid_keys.keys()))# User-defined text frames
from mutagen.id3 import TXXX
custom_text = TXXX(
encoding=3,
desc="Custom Field",
text=["Custom Value"]
)
mp3_file["TXXX:Custom Field"] = custom_text
# User-defined URL frames
from mutagen.id3 import WXXX
custom_url = WXXX(
encoding=3,
desc="Band Website",
url="https://example.com"
)
mp3_file["WXXX:Band Website"] = custom_url
# General encapsulated object
from mutagen.id3 import GEOB
embedded_file = GEOB(
encoding=3,
mime="text/plain",
filename="lyrics.txt",
desc="Song Lyrics",
data=b"Lyric text content here"
)
mp3_file["GEOB:"] = embedded_file# Multiple comment frames with different descriptions
from mutagen.id3 import COMM
mp3_file["COMM::eng"] = COMM(
encoding=3, lang="eng", desc="", text=["Main comment"]
)
mp3_file["COMM:Review:eng"] = COMM(
encoding=3, lang="eng", desc="Review", text=["Album review text"]
)
# Multiple attached pictures
mp3_file["APIC:"] = APIC(type=PictureType.COVER_FRONT, ...)
mp3_file["APIC:Back"] = APIC(type=PictureType.COVER_BACK, ...)# Force specific ID3 version when saving
mp3_file.save(v2_version=3) # Save as ID3v2.3
mp3_file.save(v2_version=4) # Save as ID3v2.4
# Handle version-specific frames
tags = mp3_file.tags
if tags.version >= (2, 4):
# Use TDRC for date in ID3v2.4+
tags["TDRC"] = "2023-03-15"
else:
# Use TYER for date in ID3v2.3
tags["TYER"] = "2023"
# Convert between versions
tags.update_to_v24() # Convert to v2.4 frame names
tags.update_to_v23() # Convert to v2.3 frame namesfrom mutagen.id3 import ID3NoHeaderError, ID3UnsupportedVersionError
try:
mp3_file = MP3("song.mp3")
except ID3NoHeaderError:
print("No ID3 header found")
# Add tags if needed
mp3_file.add_tags()
except ID3UnsupportedVersionError as e:
print(f"Unsupported ID3 version: {e}")
except MutagenError as e:
print(f"ID3 error: {e}")from mutagen.mp3 import MP3
from mutagen.id3 import TIT2, TPE1, TALB, TRCK, TDRC, TCON, APIC, PictureType
# Load MP3
mp3_file = MP3("song.mp3")
# Add basic tags
mp3_file["TIT2"] = TIT2(encoding=3, text=["Song Title"])
mp3_file["TPE1"] = TPE1(encoding=3, text=["Artist Name"])
mp3_file["TALB"] = TALB(encoding=3, text=["Album Name"])
mp3_file["TRCK"] = TRCK(encoding=3, text=["1/12"])
mp3_file["TDRC"] = TDRC(encoding=3, text=["2023"])
mp3_file["TCON"] = TCON(encoding=3, text=["Rock"])
# Add cover art
with open("cover.jpg", "rb") as f:
cover_data = f.read()
mp3_file["APIC:"] = APIC(
encoding=3,
mime="image/jpeg",
type=PictureType.COVER_FRONT,
desc="Cover",
data=cover_data
)
# Save with ID3v2.4
mp3_file.save(v2_version=4)
print("MP3 tagged successfully!")def tag_mp3_file(filename, metadata):
"""Tag MP3 file with metadata dict."""
mp3_file = MP3(filename)
# Ensure tags exist
if mp3_file.tags is None:
mp3_file.add_tags()
# Map common fields to ID3 frames
frame_mapping = {
'title': 'TIT2',
'artist': 'TPE1',
'album': 'TALB',
'date': 'TDRC',
'track': 'TRCK',
'genre': 'TCON'
}
for field, frame_id in frame_mapping.items():
if field in metadata:
mp3_file[frame_id] = getattr(mutagen.id3, frame_id)(
encoding=3, text=[metadata[field]]
)
mp3_file.save()Install with Tessl CLI
npx tessl i tessl/pypi-mutagen