CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-tinytag

Read audio file metadata

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

TinyTag

TinyTag is a pure Python library for reading metadata from various audio file formats including MP3, M4A, WAVE, OGG, FLAC, WMA, and AIFF. It provides a unified API across all supported formats, allowing developers to extract essential metadata such as title, artist, album, track numbers, duration, bitrate, and embedded images without requiring external dependencies.

Package Information

  • Package Name: tinytag
  • Language: Python
  • Installation: pip install tinytag
  • Python Version: 3.7+
  • License: MIT

Core Imports

from tinytag import TinyTag

Complete imports for all functionality:

from tinytag import (
    TinyTag, Image, Images, OtherFields, OtherImages,
    TinyTagException, ParseError, UnsupportedFormatError,
    __version__
)

For type annotations (when needed):

from typing import BinaryIO, Any
from os import PathLike

Basic Usage

from tinytag import TinyTag

# Read basic metadata
tag = TinyTag.get('/path/to/audio/file.mp3')

print(f'Title: {tag.title}')
print(f'Artist: {tag.artist}')
print(f'Album: {tag.album}')
print(f'Duration: {tag.duration:.2f} seconds')
print(f'Bitrate: {tag.bitrate} kbps')

# Read metadata with images
tag = TinyTag.get('/path/to/audio/file.mp3', image=True)
if tag.images.front_cover:
    cover_data = tag.images.front_cover.data
    with open('cover.jpg', 'wb') as f:
        f.write(cover_data)

# Check if file is supported
if TinyTag.is_supported('/path/to/file.mp3'):
    tag = TinyTag.get('/path/to/file.mp3')

Capabilities

Audio File Metadata Reading

Parse audio files and extract metadata using a unified API across all supported formats.

class TinyTag:
    @classmethod
    def get(cls,
            filename: bytes | str | PathLike[Any] | None = None,
            file_obj: BinaryIO | None = None,
            tags: bool = True,
            duration: bool = True,
            image: bool = False,
            encoding: str | None = None,
            ignore_errors: bool | None = None) -> TinyTag:
        """
        Return a TinyTag instance for an audio file.
        
        Args:
            filename: Path to audio file
            file_obj: Binary file object (alternative to filename)
            tags: Whether to parse metadata tags
            duration: Whether to calculate audio duration
            image: Whether to load embedded images
            encoding: Text encoding override for some formats
            ignore_errors: Deprecated parameter
            
        Returns:
            TinyTag instance with parsed metadata
            
        Raises:
            ParseError: If parsing fails
            ValueError: If neither filename nor file_obj provided
        """
    
    @classmethod
    def is_supported(cls, filename: bytes | str | PathLike[Any]) -> bool:
        """
        Check if a file is supported based on its extension.
        
        Args:
            filename: Path to file to check
            
        Returns:
            True if file extension is supported
        """
    
    SUPPORTED_FILE_EXTENSIONS: tuple[str, ...] = (
        '.mp1', '.mp2', '.mp3',
        '.oga', '.ogg', '.opus', '.spx',
        '.wav', '.flac', '.wma',
        '.m4b', '.m4a', '.m4r', '.m4v', '.mp4', '.aax', '.aaxc',
        '.aiff', '.aifc', '.aif', '.afc'
    )

Metadata Access

Access standard metadata fields and file properties through instance attributes.

# Audio Properties
filename: str | None          # Original filename
filesize: int                 # File size in bytes
duration: float | None        # Duration in seconds
channels: int | None          # Number of audio channels
bitrate: float | None         # Bitrate in kBits/s
bitdepth: int | None          # Bit depth (for lossless)
samplerate: int | None        # Sample rate in Hz

# Metadata Fields
artist: str | None            # Primary artist
albumartist: str | None       # Album artist
composer: str | None          # Composer
album: str | None             # Album title
disc: int | None              # Disc number
disc_total: int | None        # Total discs
title: str | None             # Track title
track: int | None             # Track number
track_total: int | None       # Total tracks
genre: str | None             # Genre
year: str | None              # Year or date
comment: str | None           # Comment field

# Additional Data
images: Images                # Embedded images
other: OtherFields           # Additional metadata

Dictionary Export

Convert metadata to dictionary format for serialization or processing.

def as_dict(self) -> dict[str, str | float | list[str]]:
    """
    Return flat dictionary representation of metadata.
    
    Returns:
        Dictionary with metadata fields, lists for multiple values
    """

# Deprecated Methods (will be removed in future versions)
def get_image(self) -> bytes | None:
    """
    DEPRECATED: Use images.any instead.
    Return bytes of any embedded image.
    
    Returns:
        Image data bytes or None if no image available
    """

@property
def extra(self) -> dict[str, str]:
    """
    DEPRECATED: Use 'other' attribute instead.
    Legacy access to additional metadata fields.
    
    Returns:
        Dictionary of extra metadata fields
    """

@property
def audio_offset(self) -> None:
    """
    OBSOLETE: Always returns None.
    This property is no longer used and will be removed.
    
    Returns:
        None
    """

Image Handling

Access embedded images like album artwork with full metadata support.

class Images:
    """Container for embedded images in audio files."""
    
    front_cover: Image | None    # Front cover image
    back_cover: Image | None     # Back cover image  
    media: Image | None          # Media/CD label image
    other: OtherImages          # Additional images
    
    @property
    def any(self) -> Image | None:
        """
        Return any available image, prioritizing front cover.
        
        Returns:
            First available image or None
        """
    
    def as_dict(self) -> dict[str, list[Image]]:
        """
        Return flat dictionary of all images.
        
        Returns:
            Dictionary mapping image types to lists of Image objects
        """

class Image:
    """Represents an embedded image in an audio file."""
    
    def __init__(self, name: str, data: bytes, mime_type: str | None = None) -> None:
        """
        Create Image instance.
        
        Args:
            name: Image type/name
            data: Binary image data
            mime_type: MIME type (e.g., 'image/jpeg')
        """
    
    name: str                    # Image type/name
    data: bytes                  # Binary image data
    mime_type: str | None        # MIME type
    description: str | None      # Image description

Additional Metadata

Access format-specific and extended metadata through standardized field names.

class OtherFields(dict[str, list[str]]):
    """Dictionary containing additional metadata fields."""
    
    # Standardized field names (when available):
    # barcode, bpm, catalog_number, conductor, copyright, director
    # encoded_by, encoder_settings, initial_key, isrc, language, license
    # lyricist, lyrics, media, publisher, set_subtitle, url

class OtherImages(dict[str, list[Image]]):
    """Dictionary containing additional embedded images."""
    
    # Standardized image types (when available):
    # generic, icon, alt_icon, front_cover, back_cover, media, leaflet
    # lead_artist, artist, conductor, band, composer, lyricist
    # recording_location, during_recording, during_performance, screen_capture
    # bright_colored_fish, illustration, band_logo, publisher_logo, unknown

Exception Handling

Handle errors during audio file parsing with specific exception types.

class TinyTagException(Exception):
    """Base exception for all TinyTag errors."""

class ParseError(TinyTagException):
    """Raised when parsing an audio file fails."""

class UnsupportedFormatError(TinyTagException):
    """Raised when file format is not supported."""

Version Information

Access the library version for compatibility checks.

__version__: str  # Current library version (e.g., "2.1.2")

Command Line Interface

Use tinytag from the command line to extract metadata and save images.

# Basic usage
python -m tinytag /path/to/audio.mp3

# Save cover image  
python -m tinytag -i cover.jpg /path/to/audio.mp3

# Different output formats
python -m tinytag -f csv /path/to/audio.mp3
python -m tinytag -f json /path/to/audio.mp3

# Skip unsupported files
python -m tinytag -s /path/to/files/*
# Command line options:
# -h, --help                    Display help
# -i, --save-image <path>       Save cover art to file  
# -f, --format json|csv|tsv|tabularcsv  Output format
# -s, --skip-unsupported        Skip unsupported files

Usage Examples

Basic Metadata Extraction

from tinytag import TinyTag

# Parse audio file
tag = TinyTag.get('music.mp3')

# Access common metadata
print(f'Artist: {tag.artist}')
print(f'Title: {tag.title}')
print(f'Album: {tag.album}')
print(f'Duration: {tag.duration:.1f}s')
print(f'Bitrate: {tag.bitrate}kbps')

# Check audio properties
if tag.channels:
    print(f'Channels: {tag.channels}')
if tag.samplerate:
    print(f'Sample rate: {tag.samplerate}Hz')

Working with Images

from tinytag import TinyTag, Image

# Load file with images
tag = TinyTag.get('album.flac', image=True)

# Access front cover
if tag.images.front_cover:
    cover = tag.images.front_cover
    print(f'Cover: {cover.name} ({cover.mime_type})')
    print(f'Size: {len(cover.data)} bytes')
    
    # Save cover image
    with open('cover.jpg', 'wb') as f:
        f.write(cover.data)

# Get any available image
any_image = tag.images.any
if any_image:
    print(f'Found image: {any_image.name}')

# Legacy approach (deprecated - use images.any instead)
# image_data = tag.get_image()  # DEPRECATED
# if image_data:
#     with open('cover_legacy.jpg', 'wb') as f:
#         f.write(image_data)

# Access all images
all_images = tag.images.as_dict()
for image_type, images in all_images.items():
    print(f'{image_type}: {len(images)} images')

Additional Metadata

from tinytag import TinyTag

tag = TinyTag.get('detailed.mp3')

# Access additional fields
bpm_values = tag.other.get('bpm')
if bpm_values:
    print(f'BPM: {bpm_values[0]}')

lyrics = tag.other.get('lyrics')
if lyrics:
    print(f'Lyrics: {lyrics[0][:100]}...')

# Handle multiple artists
primary_artist = tag.artist
additional_artists = tag.other.get('artist', [])
all_artists = [primary_artist] + additional_artists if primary_artist else additional_artists
print(f'All artists: {", ".join(all_artists)}')

# Legacy metadata access (deprecated - use 'other' instead)
# extra_data = tag.extra  # DEPRECATED: returns dict[str, str]
# Use tag.other instead which returns dict[str, list[str]]

# Export all metadata
metadata = tag.as_dict()
for field, value in metadata.items():
    print(f'{field}: {value}')

Error Handling

from tinytag import TinyTag, ParseError, UnsupportedFormatError

try:
    # Check support first
    if not TinyTag.is_supported('file.unknown'):
        print('File format not supported')
        exit(1)
    
    # Parse file
    tag = TinyTag.get('file.mp3')
    print(f'Successfully parsed: {tag.title}')
    
except ParseError as e:
    print(f'Failed to parse file: {e}')
except UnsupportedFormatError as e:
    print(f'Unsupported format: {e}')
except Exception as e:
    print(f'Unexpected error: {e}')

File Object Usage

from tinytag import TinyTag
from io import BytesIO

# Using file object instead of filename
with open('audio.mp3', 'rb') as f:
    tag = TinyTag.get(file_obj=f)
    print(f'Title: {tag.title}')

# Using BytesIO
with open('audio.mp3', 'rb') as f:
    data = f.read()
    
file_obj = BytesIO(data)
tag = TinyTag.get(file_obj=file_obj)
print(f'Duration: {tag.duration}')

Version Information

from tinytag import __version__

# Check library version
print(f'TinyTag version: {__version__}')

# Version-dependent behavior
from packaging import version
if version.parse(__version__) >= version.parse('2.0.0'):
    # Use v2.x API features
    tag = TinyTag.get('file.mp3')
    if tag.track is not None:  # v2.x: integers
        print(f'Track: {tag.track}')
else:
    # Handle v1.x compatibility
    pass

Supported Formats

TinyTag supports the following audio formats:

  • MP3/MP2/MP1: ID3 v1, v1.1, v2.2, v2.3+
  • M4A: AAC and ALAC encoding
  • WAVE/WAV: Standard PCM and compressed
  • OGG: FLAC, Opus, Speex, Vorbis
  • FLAC: Free Lossless Audio Codec
  • WMA: Windows Media Audio
  • AIFF/AIFF-C: Audio Interchange File Format

All formats use the same unified API, ensuring consistent behavior across different audio file types.

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
pypipkg:pypi/tinytag@2.1.x
Publish Source
CLI
Badge
tessl/pypi-tinytag badge