CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-mutagen

Python library for handling audio metadata across multiple formats including MP3, FLAC, MP4, OGG and many others

Pending
Overview
Eval results
Files

mp3-id3.mddocs/

MP3 and ID3 Tags

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.

Imports

# 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 EasyID3

MP3 Format

MP3 Class

class 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")

MPEG Stream Information

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}")

ID3v2 Tags

ID3 Container

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")

Frame Classes

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()

Easy ID3 Interface

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()))

Advanced ID3 Features

Custom Frames

# 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 Frame Instances

# 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, ...)

ID3 Version Handling

# 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 names

Error Handling

from 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}")

Practical Examples

Complete MP3 Tagging

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!")

Cross-Format Compatibility

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()

See Also

  • Mutagen - Main documentation and overview
  • Easy Interfaces - Simplified cross-format tag access
  • Core Functionality - Base classes and common patterns
  • Container Formats - MP4 and other container formats

Install with Tessl CLI

npx tessl i tessl/pypi-mutagen

docs

container-formats.md

core-functionality.md

easy-interfaces.md

index.md

lossless-formats.md

mp3-id3.md

ogg-formats.md

tile.json