0
# TinyTag
1
2
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.
3
4
## Package Information
5
6
- **Package Name**: tinytag
7
- **Language**: Python
8
- **Installation**: `pip install tinytag`
9
- **Python Version**: 3.7+
10
- **License**: MIT
11
12
## Core Imports
13
14
```python
15
from tinytag import TinyTag
16
```
17
18
Complete imports for all functionality:
19
20
```python
21
from tinytag import (
22
TinyTag, Image, Images, OtherFields, OtherImages,
23
TinyTagException, ParseError, UnsupportedFormatError,
24
__version__
25
)
26
```
27
28
For type annotations (when needed):
29
30
```python
31
from typing import BinaryIO, Any
32
from os import PathLike
33
```
34
35
## Basic Usage
36
37
```python
38
from tinytag import TinyTag
39
40
# Read basic metadata
41
tag = TinyTag.get('/path/to/audio/file.mp3')
42
43
print(f'Title: {tag.title}')
44
print(f'Artist: {tag.artist}')
45
print(f'Album: {tag.album}')
46
print(f'Duration: {tag.duration:.2f} seconds')
47
print(f'Bitrate: {tag.bitrate} kbps')
48
49
# Read metadata with images
50
tag = TinyTag.get('/path/to/audio/file.mp3', image=True)
51
if tag.images.front_cover:
52
cover_data = tag.images.front_cover.data
53
with open('cover.jpg', 'wb') as f:
54
f.write(cover_data)
55
56
# Check if file is supported
57
if TinyTag.is_supported('/path/to/file.mp3'):
58
tag = TinyTag.get('/path/to/file.mp3')
59
```
60
61
## Capabilities
62
63
### Audio File Metadata Reading
64
65
Parse audio files and extract metadata using a unified API across all supported formats.
66
67
```python { .api }
68
class TinyTag:
69
@classmethod
70
def get(cls,
71
filename: bytes | str | PathLike[Any] | None = None,
72
file_obj: BinaryIO | None = None,
73
tags: bool = True,
74
duration: bool = True,
75
image: bool = False,
76
encoding: str | None = None,
77
ignore_errors: bool | None = None) -> TinyTag:
78
"""
79
Return a TinyTag instance for an audio file.
80
81
Args:
82
filename: Path to audio file
83
file_obj: Binary file object (alternative to filename)
84
tags: Whether to parse metadata tags
85
duration: Whether to calculate audio duration
86
image: Whether to load embedded images
87
encoding: Text encoding override for some formats
88
ignore_errors: Deprecated parameter
89
90
Returns:
91
TinyTag instance with parsed metadata
92
93
Raises:
94
ParseError: If parsing fails
95
ValueError: If neither filename nor file_obj provided
96
"""
97
98
@classmethod
99
def is_supported(cls, filename: bytes | str | PathLike[Any]) -> bool:
100
"""
101
Check if a file is supported based on its extension.
102
103
Args:
104
filename: Path to file to check
105
106
Returns:
107
True if file extension is supported
108
"""
109
110
SUPPORTED_FILE_EXTENSIONS: tuple[str, ...] = (
111
'.mp1', '.mp2', '.mp3',
112
'.oga', '.ogg', '.opus', '.spx',
113
'.wav', '.flac', '.wma',
114
'.m4b', '.m4a', '.m4r', '.m4v', '.mp4', '.aax', '.aaxc',
115
'.aiff', '.aifc', '.aif', '.afc'
116
)
117
```
118
119
### Metadata Access
120
121
Access standard metadata fields and file properties through instance attributes.
122
123
```python { .api }
124
# Audio Properties
125
filename: str | None # Original filename
126
filesize: int # File size in bytes
127
duration: float | None # Duration in seconds
128
channels: int | None # Number of audio channels
129
bitrate: float | None # Bitrate in kBits/s
130
bitdepth: int | None # Bit depth (for lossless)
131
samplerate: int | None # Sample rate in Hz
132
133
# Metadata Fields
134
artist: str | None # Primary artist
135
albumartist: str | None # Album artist
136
composer: str | None # Composer
137
album: str | None # Album title
138
disc: int | None # Disc number
139
disc_total: int | None # Total discs
140
title: str | None # Track title
141
track: int | None # Track number
142
track_total: int | None # Total tracks
143
genre: str | None # Genre
144
year: str | None # Year or date
145
comment: str | None # Comment field
146
147
# Additional Data
148
images: Images # Embedded images
149
other: OtherFields # Additional metadata
150
```
151
152
### Dictionary Export
153
154
Convert metadata to dictionary format for serialization or processing.
155
156
```python { .api }
157
def as_dict(self) -> dict[str, str | float | list[str]]:
158
"""
159
Return flat dictionary representation of metadata.
160
161
Returns:
162
Dictionary with metadata fields, lists for multiple values
163
"""
164
165
# Deprecated Methods (will be removed in future versions)
166
def get_image(self) -> bytes | None:
167
"""
168
DEPRECATED: Use images.any instead.
169
Return bytes of any embedded image.
170
171
Returns:
172
Image data bytes or None if no image available
173
"""
174
175
@property
176
def extra(self) -> dict[str, str]:
177
"""
178
DEPRECATED: Use 'other' attribute instead.
179
Legacy access to additional metadata fields.
180
181
Returns:
182
Dictionary of extra metadata fields
183
"""
184
185
@property
186
def audio_offset(self) -> None:
187
"""
188
OBSOLETE: Always returns None.
189
This property is no longer used and will be removed.
190
191
Returns:
192
None
193
"""
194
```
195
196
### Image Handling
197
198
Access embedded images like album artwork with full metadata support.
199
200
```python { .api }
201
class Images:
202
"""Container for embedded images in audio files."""
203
204
front_cover: Image | None # Front cover image
205
back_cover: Image | None # Back cover image
206
media: Image | None # Media/CD label image
207
other: OtherImages # Additional images
208
209
@property
210
def any(self) -> Image | None:
211
"""
212
Return any available image, prioritizing front cover.
213
214
Returns:
215
First available image or None
216
"""
217
218
def as_dict(self) -> dict[str, list[Image]]:
219
"""
220
Return flat dictionary of all images.
221
222
Returns:
223
Dictionary mapping image types to lists of Image objects
224
"""
225
226
class Image:
227
"""Represents an embedded image in an audio file."""
228
229
def __init__(self, name: str, data: bytes, mime_type: str | None = None) -> None:
230
"""
231
Create Image instance.
232
233
Args:
234
name: Image type/name
235
data: Binary image data
236
mime_type: MIME type (e.g., 'image/jpeg')
237
"""
238
239
name: str # Image type/name
240
data: bytes # Binary image data
241
mime_type: str | None # MIME type
242
description: str | None # Image description
243
```
244
245
### Additional Metadata
246
247
Access format-specific and extended metadata through standardized field names.
248
249
```python { .api }
250
class OtherFields(dict[str, list[str]]):
251
"""Dictionary containing additional metadata fields."""
252
253
# Standardized field names (when available):
254
# barcode, bpm, catalog_number, conductor, copyright, director
255
# encoded_by, encoder_settings, initial_key, isrc, language, license
256
# lyricist, lyrics, media, publisher, set_subtitle, url
257
258
class OtherImages(dict[str, list[Image]]):
259
"""Dictionary containing additional embedded images."""
260
261
# Standardized image types (when available):
262
# generic, icon, alt_icon, front_cover, back_cover, media, leaflet
263
# lead_artist, artist, conductor, band, composer, lyricist
264
# recording_location, during_recording, during_performance, screen_capture
265
# bright_colored_fish, illustration, band_logo, publisher_logo, unknown
266
```
267
268
### Exception Handling
269
270
Handle errors during audio file parsing with specific exception types.
271
272
```python { .api }
273
class TinyTagException(Exception):
274
"""Base exception for all TinyTag errors."""
275
276
class ParseError(TinyTagException):
277
"""Raised when parsing an audio file fails."""
278
279
class UnsupportedFormatError(TinyTagException):
280
"""Raised when file format is not supported."""
281
```
282
283
### Version Information
284
285
Access the library version for compatibility checks.
286
287
```python { .api }
288
__version__: str # Current library version (e.g., "2.1.2")
289
```
290
291
### Command Line Interface
292
293
Use tinytag from the command line to extract metadata and save images.
294
295
```bash
296
# Basic usage
297
python -m tinytag /path/to/audio.mp3
298
299
# Save cover image
300
python -m tinytag -i cover.jpg /path/to/audio.mp3
301
302
# Different output formats
303
python -m tinytag -f csv /path/to/audio.mp3
304
python -m tinytag -f json /path/to/audio.mp3
305
306
# Skip unsupported files
307
python -m tinytag -s /path/to/files/*
308
```
309
310
```python { .api }
311
# Command line options:
312
# -h, --help Display help
313
# -i, --save-image <path> Save cover art to file
314
# -f, --format json|csv|tsv|tabularcsv Output format
315
# -s, --skip-unsupported Skip unsupported files
316
```
317
318
## Usage Examples
319
320
### Basic Metadata Extraction
321
322
```python
323
from tinytag import TinyTag
324
325
# Parse audio file
326
tag = TinyTag.get('music.mp3')
327
328
# Access common metadata
329
print(f'Artist: {tag.artist}')
330
print(f'Title: {tag.title}')
331
print(f'Album: {tag.album}')
332
print(f'Duration: {tag.duration:.1f}s')
333
print(f'Bitrate: {tag.bitrate}kbps')
334
335
# Check audio properties
336
if tag.channels:
337
print(f'Channels: {tag.channels}')
338
if tag.samplerate:
339
print(f'Sample rate: {tag.samplerate}Hz')
340
```
341
342
### Working with Images
343
344
```python
345
from tinytag import TinyTag, Image
346
347
# Load file with images
348
tag = TinyTag.get('album.flac', image=True)
349
350
# Access front cover
351
if tag.images.front_cover:
352
cover = tag.images.front_cover
353
print(f'Cover: {cover.name} ({cover.mime_type})')
354
print(f'Size: {len(cover.data)} bytes')
355
356
# Save cover image
357
with open('cover.jpg', 'wb') as f:
358
f.write(cover.data)
359
360
# Get any available image
361
any_image = tag.images.any
362
if any_image:
363
print(f'Found image: {any_image.name}')
364
365
# Legacy approach (deprecated - use images.any instead)
366
# image_data = tag.get_image() # DEPRECATED
367
# if image_data:
368
# with open('cover_legacy.jpg', 'wb') as f:
369
# f.write(image_data)
370
371
# Access all images
372
all_images = tag.images.as_dict()
373
for image_type, images in all_images.items():
374
print(f'{image_type}: {len(images)} images')
375
```
376
377
### Additional Metadata
378
379
```python
380
from tinytag import TinyTag
381
382
tag = TinyTag.get('detailed.mp3')
383
384
# Access additional fields
385
bpm_values = tag.other.get('bpm')
386
if bpm_values:
387
print(f'BPM: {bpm_values[0]}')
388
389
lyrics = tag.other.get('lyrics')
390
if lyrics:
391
print(f'Lyrics: {lyrics[0][:100]}...')
392
393
# Handle multiple artists
394
primary_artist = tag.artist
395
additional_artists = tag.other.get('artist', [])
396
all_artists = [primary_artist] + additional_artists if primary_artist else additional_artists
397
print(f'All artists: {", ".join(all_artists)}')
398
399
# Legacy metadata access (deprecated - use 'other' instead)
400
# extra_data = tag.extra # DEPRECATED: returns dict[str, str]
401
# Use tag.other instead which returns dict[str, list[str]]
402
403
# Export all metadata
404
metadata = tag.as_dict()
405
for field, value in metadata.items():
406
print(f'{field}: {value}')
407
```
408
409
### Error Handling
410
411
```python
412
from tinytag import TinyTag, ParseError, UnsupportedFormatError
413
414
try:
415
# Check support first
416
if not TinyTag.is_supported('file.unknown'):
417
print('File format not supported')
418
exit(1)
419
420
# Parse file
421
tag = TinyTag.get('file.mp3')
422
print(f'Successfully parsed: {tag.title}')
423
424
except ParseError as e:
425
print(f'Failed to parse file: {e}')
426
except UnsupportedFormatError as e:
427
print(f'Unsupported format: {e}')
428
except Exception as e:
429
print(f'Unexpected error: {e}')
430
```
431
432
### File Object Usage
433
434
```python
435
from tinytag import TinyTag
436
from io import BytesIO
437
438
# Using file object instead of filename
439
with open('audio.mp3', 'rb') as f:
440
tag = TinyTag.get(file_obj=f)
441
print(f'Title: {tag.title}')
442
443
# Using BytesIO
444
with open('audio.mp3', 'rb') as f:
445
data = f.read()
446
447
file_obj = BytesIO(data)
448
tag = TinyTag.get(file_obj=file_obj)
449
print(f'Duration: {tag.duration}')
450
```
451
452
### Version Information
453
454
```python
455
from tinytag import __version__
456
457
# Check library version
458
print(f'TinyTag version: {__version__}')
459
460
# Version-dependent behavior
461
from packaging import version
462
if version.parse(__version__) >= version.parse('2.0.0'):
463
# Use v2.x API features
464
tag = TinyTag.get('file.mp3')
465
if tag.track is not None: # v2.x: integers
466
print(f'Track: {tag.track}')
467
else:
468
# Handle v1.x compatibility
469
pass
470
```
471
472
## Supported Formats
473
474
TinyTag supports the following audio formats:
475
476
- **MP3/MP2/MP1**: ID3 v1, v1.1, v2.2, v2.3+
477
- **M4A**: AAC and ALAC encoding
478
- **WAVE/WAV**: Standard PCM and compressed
479
- **OGG**: FLAC, Opus, Speex, Vorbis
480
- **FLAC**: Free Lossless Audio Codec
481
- **WMA**: Windows Media Audio
482
- **AIFF/AIFF-C**: Audio Interchange File Format
483
484
All formats use the same unified API, ensuring consistent behavior across different audio file types.