or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.md

index.mddocs/

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.