or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

container-formats.mdcore-functionality.mdeasy-interfaces.mdindex.mdlossless-formats.mdmp3-id3.mdogg-formats.md

easy-interfaces.mddocs/

0

# Easy Interfaces

1

2

This document covers Mutagen's easy interfaces that provide simplified, normalized tag access across different audio formats. Easy interfaces abstract away format-specific tag names and provide dictionary-like access with common field names.

3

4

## Imports

5

6

```python { .api }

7

# Easy ID3 interface for MP3 files

8

from mutagen.easyid3 import EasyID3

9

10

# Easy MP4 interface for M4A files

11

from mutagen.easymp4 import EasyMP4, EasyMP4Tags

12

13

# Easy TrueAudio interface

14

from mutagen.trueaudio import EasyTrueAudio

15

16

# Easy MP3 class (combines MP3 with EasyID3)

17

from mutagen.mp3 import EasyMP3

18

19

# Base easy functionality

20

from mutagen._util import DictMixin

21

```

22

23

## Overview

24

25

Easy interfaces solve the problem of format-specific tag names by providing normalized field names that work consistently across different audio formats:

26

27

**Problem**: Different formats use different tag names

28

- MP3 (ID3): `TIT2` for title, `TPE1` for artist

29

- FLAC (Vorbis): `TITLE` for title, `ARTIST` for artist

30

- MP4 (iTunes): `©nam` for title, `©ART` for artist

31

32

**Solution**: Easy interfaces use common names

33

- `title` for title across all formats

34

- `artist` for artist across all formats

35

- Automatic conversion to/from format-specific names

36

37

## EasyID3 Interface

38

39

### EasyID3 Class

40

41

```python { .api }

42

class EasyID3:

43

"""Easy dictionary-like interface for ID3 tags.

44

45

Provides simplified tag names instead of ID3 frame IDs.

46

Automatically handles encoding and multi-value conversion.

47

48

Attributes:

49

valid_keys: Dict mapping easy names to ID3 frame names

50

filename: Path to audio file

51

"""

52

53

def __init__(self, filename: str = None) -> None:

54

"""Create EasyID3 instance.

55

56

Args:

57

filename: Path to MP3 file (optional)

58

59

Raises:

60

MutagenError: If file is not valid MP3 or has no ID3 tags

61

"""

62

63

def save(self, **kwargs) -> None:

64

"""Save ID3 tags back to file."""

65

66

def delete(self) -> None:

67

"""Remove all ID3 tags from file."""

68

69

def add_tags(self) -> None:

70

"""Add empty ID3 tags if none exist."""

71

72

# Standard easy field mappings for ID3 (alphabetical order)

73

EASY_ID3_FIELDS = {

74

'album': 'TALB', # Album title

75

'albumartist': 'TPE2', # Album artist

76

'albumartistsort': 'TSO2', # Album artist sort order

77

'albumsort': 'TSOA', # Album sort order

78

'arranger': 'TPE4', # Arranger

79

'artist': 'TPE1', # Lead artist

80

'artistsort': 'TSOP', # Artist sort order

81

'author': 'TOLY', # Original lyricist

82

'bpm': 'TBPM', # Beats per minute

83

'compilation': 'TCMP', # iTunes compilation flag

84

'composer': 'TCOM', # Composer

85

'composersort': 'TSOC', # Composer sort order

86

'conductor': 'TPE3', # Conductor

87

'copyright': 'TCOP', # Copyright message

88

'date': 'TDRC', # Recording date (special handling)

89

'discnumber': 'TPOS', # Disc number

90

'discsubtitle': 'TSST', # Set subtitle

91

'encodedby': 'TENC', # Encoded by

92

'genre': 'TCON', # Genre (special handling)

93

'grouping': 'TIT1', # Content group description

94

'isrc': 'TSRC', # ISRC code

95

'language': 'TLAN', # Language

96

'length': 'TLEN', # Length in milliseconds

97

'lyricist': 'TEXT', # Lyricist

98

'media': 'TMED', # Media type

99

'mood': 'TMOO', # Mood

100

'organization': 'TPUB', # Publisher

101

'originaldate': 'TDOR', # Original release date (special handling)

102

'title': 'TIT2', # Title

103

'titlesort': 'TSOT', # Title sort order

104

'tracknumber': 'TRCK', # Track number

105

'version': 'TIT3', # Subtitle/description refinement

106

}

107

108

# Usage examples

109

from mutagen.easyid3 import EasyID3

110

111

# Load MP3 with easy interface

112

easy_tags = EasyID3("song.mp3")

113

114

# Simple tag access with normalized names

115

easy_tags["title"] = ["Song Title"]

116

easy_tags["artist"] = ["Artist Name"]

117

easy_tags["album"] = ["Album Name"]

118

easy_tags["date"] = ["2023"]

119

easy_tags["tracknumber"] = ["1/12"] # Track 1 of 12

120

easy_tags["discnumber"] = ["1/2"] # Disc 1 of 2

121

122

# Multi-value fields

123

easy_tags["genre"] = ["Rock", "Alternative"]

124

easy_tags["artist"] = ["Primary Artist", "Featured Artist"]

125

126

# Additional metadata

127

easy_tags["albumartist"] = ["Album Artist"]

128

easy_tags["composer"] = ["Composer Name"]

129

easy_tags["bpm"] = ["120"]

130

easy_tags["comment"] = ["This is a comment"]

131

132

# Save changes

133

easy_tags.save()

134

135

# Read tags

136

print(f"Title: {easy_tags['title'][0]}")

137

print(f"Artists: {', '.join(easy_tags['artist'])}")

138

139

# List available keys

140

print("Available keys:", list(EasyID3.valid_keys.keys()))

141

142

# Check if key exists

143

if "title" in easy_tags:

144

print(f"Title: {easy_tags['title'][0]}")

145

```

146

147

### EasyMP3 Class

148

149

```python { .api }

150

class EasyMP3:

151

"""MP3 file with EasyID3 interface built-in.

152

153

Combines MP3 format support with EasyID3 tag interface.

154

Acts like MP3 class but uses easy tag names.

155

"""

156

157

def __init__(self, filename: str) -> None:

158

"""Load MP3 file with easy tag interface."""

159

160

# Usage examples

161

from mutagen.mp3 import EasyMP3

162

163

# Load MP3 with built-in easy interface

164

mp3_file = EasyMP3("song.mp3")

165

166

# Access stream info like regular MP3

167

print(f"Duration: {mp3_file.info.length} seconds")

168

print(f"Bitrate: {mp3_file.info.bitrate} bps")

169

170

# Use easy tag names

171

mp3_file["title"] = ["Song Title"]

172

mp3_file["artist"] = ["Artist Name"]

173

mp3_file.save()

174

175

# Auto-detection with easy interface

176

import mutagen

177

easy_mp3 = mutagen.File("song.mp3", easy=True) # Returns EasyMP3

178

```

179

180

## EasyMP4 Interface

181

182

### EasyMP4 Class

183

184

```python { .api }

185

class EasyMP4:

186

"""Easy dictionary-like interface for MP4 metadata.

187

188

Provides simplified tag names instead of iTunes atom names.

189

Automatically handles data type conversion and encoding.

190

"""

191

192

def __init__(self, filename: str) -> None:

193

"""Create EasyMP4 instance.

194

195

Args:

196

filename: Path to MP4 file (.m4a, .m4b, .m4p, etc.)

197

"""

198

199

class EasyMP4Tags:

200

"""Easy MP4 tags container with normalized field names."""

201

202

# Standard easy field mappings for MP4

203

EASY_MP4_FIELDS = {

204

# Basic metadata

205

'title': '©nam', # Title

206

'artist': '©ART', # Artist

207

'album': '©alb', # Album

208

'albumartist': 'aART', # Album artist

209

'date': '©day', # Date

210

211

# Track/disc information

212

'tracknumber': 'trkn', # Track number (special handling)

213

'discnumber': 'disk', # Disc number (special handling)

214

215

# Classification

216

'genre': '©gen', # Genre

217

'grouping': '©grp', # Grouping

218

219

# Credits

220

'composer': '©wrt', # Writer/Composer

221

'comment': '©cmt', # Comment

222

'lyrics': '©lyr', # Lyrics

223

224

# Technical

225

'encodedby': '©too', # Encoded by

226

'copyright': '©cpy', # Copyright

227

'bpm': 'tmpo', # Beats per minute (special handling)

228

229

# Sorting (iTunes)

230

'titlesort': 'sonm', # Title sort order

231

'artistsort': 'soar', # Artist sort order

232

'albumsort': 'soal', # Album sort order

233

'albumartistsort': 'soaa', # Album artist sort order

234

}

235

236

# Usage examples

237

from mutagen.easymp4 import EasyMP4

238

239

# Load MP4 with easy interface

240

easy_mp4 = EasyMP4("song.m4a")

241

242

# Simple tag access

243

easy_mp4["title"] = ["Song Title"]

244

easy_mp4["artist"] = ["Artist Name"]

245

easy_mp4["album"] = ["Album Name"]

246

easy_mp4["date"] = ["2023"]

247

248

# Track/disc numbers (simplified)

249

easy_mp4["tracknumber"] = ["1/12"] # Automatically converts to tuple

250

easy_mp4["discnumber"] = ["1/2"] # Automatically converts to tuple

251

252

# Numeric fields

253

easy_mp4["bpm"] = ["120"] # Automatically converts to integer

254

255

# Multi-value support

256

easy_mp4["genre"] = ["Rock", "Alternative"]

257

258

# Sorting fields for iTunes

259

easy_mp4["titlesort"] = ["Title, The"] # Sort ignoring "The"

260

easy_mp4["artistsort"] = ["Smith, John"] # Last, First format

261

262

easy_mp4.save()

263

264

# Read tags

265

print(f"Title: {easy_mp4['title'][0]}")

266

print(f"Track: {easy_mp4['tracknumber'][0]}") # Returns "1/12" format

267

```

268

269

## EasyTrueAudio Interface

270

271

```python { .api }

272

class EasyTrueAudio:

273

"""TrueAudio file with EasyID3 interface.

274

275

Since TrueAudio uses ID3v2 tags, it can use the same easy

276

interface as MP3 files.

277

"""

278

279

# Usage examples

280

from mutagen.trueaudio import EasyTrueAudio

281

282

# Load TrueAudio with easy interface

283

tta_file = EasyTrueAudio("song.tta")

284

285

# Same easy field names as EasyID3

286

tta_file["title"] = ["Song Title"]

287

tta_file["artist"] = ["Artist Name"]

288

tta_file.save()

289

```

290

291

## Cross-Format Easy Usage

292

293

### Unified Tag Access

294

295

```python

296

def get_easy_tags(filename):

297

"""Get tags using easy interface regardless of format."""

298

import mutagen

299

from mutagen.easyid3 import EasyID3

300

from mutagen.easymp4 import EasyMP4

301

from mutagen.trueaudio import EasyTrueAudio

302

303

# Try auto-detection with easy interface

304

audio_file = mutagen.File(filename, easy=True)

305

306

if audio_file:

307

return audio_file

308

309

# Fallback to format-specific easy interfaces

310

try:

311

if filename.lower().endswith('.mp3'):

312

return EasyID3(filename)

313

elif filename.lower().endswith(('.m4a', '.m4b', '.m4p', '.mp4')):

314

return EasyMP4(filename)

315

elif filename.lower().endswith('.tta'):

316

return EasyTrueAudio(filename)

317

except:

318

pass

319

320

return None

321

322

def set_basic_tags(filename, metadata):

323

"""Set basic tags using easy interface."""

324

easy_file = get_easy_tags(filename)

325

326

if not easy_file:

327

print(f"No easy interface available for {filename}")

328

return False

329

330

# Standard fields that work across easy interfaces

331

field_mapping = {

332

'title': 'title',

333

'artist': 'artist',

334

'album': 'album',

335

'date': 'date',

336

'genre': 'genre',

337

'tracknumber': 'tracknumber',

338

'albumartist': 'albumartist'

339

}

340

341

for source_key, easy_key in field_mapping.items():

342

if source_key in metadata and easy_key in easy_file.valid_keys:

343

value = metadata[source_key]

344

easy_file[easy_key] = [str(value)] if not isinstance(value, list) else value

345

346

easy_file.save()

347

return True

348

349

# Usage

350

metadata = {

351

'title': 'Song Title',

352

'artist': 'Artist Name',

353

'album': 'Album Name',

354

'date': '2023',

355

'genre': 'Rock',

356

'tracknumber': '1'

357

}

358

359

# Works with MP3, MP4, TrueAudio

360

set_basic_tags("song.mp3", metadata)

361

set_basic_tags("song.m4a", metadata)

362

set_basic_tags("song.tta", metadata)

363

```

364

365

### Tag Migration Between Formats

366

367

```python

368

def migrate_tags_easy(source_file, target_file):

369

"""Migrate tags between formats using easy interfaces."""

370

371

source_easy = get_easy_tags(source_file)

372

target_easy = get_easy_tags(target_file)

373

374

if not source_easy or not target_easy:

375

return False

376

377

# Get intersection of supported fields

378

source_keys = set(source_easy.valid_keys.keys())

379

target_keys = set(target_easy.valid_keys.keys())

380

common_keys = source_keys & target_keys

381

382

# Copy common fields

383

for key in common_keys:

384

if key in source_easy:

385

target_easy[key] = source_easy[key]

386

387

target_easy.save()

388

print(f"Migrated {len(common_keys)} tag fields")

389

return True

390

391

# Example: Copy tags from MP3 to M4A

392

migrate_tags_easy("song.mp3", "song.m4a")

393

```

394

395

## Advanced Easy Interface Usage

396

397

### Custom Field Mappings

398

399

```python

400

# Extend EasyID3 with custom mappings

401

from mutagen.easyid3 import EasyID3

402

403

def add_custom_easy_mapping(easy_key, frame_id, getter=None, setter=None):

404

"""Add custom field mapping to EasyID3."""

405

406

if getter is None:

407

def getter(id3, key):

408

return [frame.text[0] for frame in id3.getall(frame_id)]

409

410

if setter is None:

411

def setter(id3, key, value):

412

from mutagen.id3 import TextFrame

413

frame_class = getattr(mutagen.id3, frame_id)

414

id3[frame_id] = frame_class(encoding=3, text=value)

415

416

EasyID3.RegisterKey(easy_key, getter, setter)

417

418

# Add custom field for mood

419

def mood_get(id3, key):

420

"""Get mood from TMOO frame."""

421

frames = id3.getall('TMOO')

422

return [frame.text[0] for frame in frames] if frames else []

423

424

def mood_set(id3, key, value):

425

"""Set mood in TMOO frame."""

426

from mutagen.id3 import TMOO

427

id3['TMOO'] = TMOO(encoding=3, text=value)

428

429

add_custom_easy_mapping('mood', 'TMOO', mood_get, mood_set)

430

431

# Now use custom field

432

easy_tags = EasyID3("song.mp3")

433

easy_tags['mood'] = ['Happy']

434

easy_tags.save()

435

```

436

437

### Validation and Normalization

438

439

```python

440

def validate_easy_tags(easy_file, required_fields=None):

441

"""Validate easy tags and normalize values."""

442

443

if required_fields is None:

444

required_fields = ['title', 'artist', 'album']

445

446

issues = []

447

448

# Check required fields

449

for field in required_fields:

450

if field not in easy_file or not easy_file[field]:

451

issues.append(f"Missing required field: {field}")

452

453

# Normalize track numbers

454

if 'tracknumber' in easy_file:

455

track_values = easy_file['tracknumber']

456

normalized_tracks = []

457

458

for track in track_values:

459

# Ensure track number format

460

if '/' not in track:

461

normalized_tracks.append(track)

462

else:

463

# Validate track/total format

464

try:

465

current, total = track.split('/', 1)

466

int(current) # Validate numeric

467

int(total) # Validate numeric

468

normalized_tracks.append(track)

469

except ValueError:

470

issues.append(f"Invalid track number format: {track}")

471

472

if normalized_tracks != track_values:

473

easy_file['tracknumber'] = normalized_tracks

474

475

# Normalize dates to YYYY format

476

if 'date' in easy_file:

477

date_values = easy_file['date']

478

normalized_dates = []

479

480

for date in date_values:

481

# Extract year from various date formats

482

import re

483

year_match = re.search(r'(\d{4})', date)

484

if year_match:

485

normalized_dates.append(year_match.group(1))

486

else:

487

issues.append(f"Invalid date format: {date}")

488

489

if normalized_dates != date_values:

490

easy_file['date'] = normalized_dates

491

492

return issues

493

494

# Usage

495

easy_tags = EasyID3("song.mp3")

496

issues = validate_easy_tags(easy_tags)

497

498

if issues:

499

print("Tag validation issues:")

500

for issue in issues:

501

print(f" - {issue}")

502

else:

503

print("All tags valid")

504

easy_tags.save()

505

```

506

507

### Batch Easy Processing

508

509

```python

510

import os

511

import mutagen

512

513

def batch_easy_tag(directory, tag_updates):

514

"""Batch update tags using easy interfaces."""

515

516

results = {'success': [], 'failed': [], 'skipped': []}

517

518

for filename in os.listdir(directory):

519

filepath = os.path.join(directory, filename)

520

521

if not os.path.isfile(filepath):

522

continue

523

524

try:

525

# Try easy interface first

526

audio_file = mutagen.File(filepath, easy=True)

527

528

if audio_file and hasattr(audio_file, 'valid_keys'):

529

# Apply updates

530

for key, value in tag_updates.items():

531

if key in audio_file.valid_keys:

532

audio_file[key] = [value] if not isinstance(value, list) else value

533

534

audio_file.save()

535

results['success'].append(filename)

536

537

else:

538

results['skipped'].append(f"{filename} (no easy interface)")

539

540

except Exception as e:

541

results['failed'].append(f"{filename} ({str(e)})")

542

543

return results

544

545

# Usage

546

tag_updates = {

547

'albumartist': 'Various Artists',

548

'date': '2023',

549

'genre': 'Compilation'

550

}

551

552

results = batch_easy_tag("/path/to/music", tag_updates)

553

554

print(f"Successfully updated: {len(results['success'])} files")

555

print(f"Failed: {len(results['failed'])} files")

556

print(f"Skipped: {len(results['skipped'])} files")

557

```

558

559

## Available Easy Fields

560

561

### Common Fields (Most Formats)

562

563

```python

564

COMMON_EASY_FIELDS = [

565

'title', # Track title

566

'artist', # Primary artist

567

'album', # Album name

568

'albumartist', # Album artist

569

'date', # Release date

570

'genre', # Musical genre

571

'tracknumber', # Track number (format: "1" or "1/12")

572

'discnumber', # Disc number (format: "1" or "1/2")

573

]

574

```

575

576

### Extended Fields (Format Dependent)

577

578

```python

579

EXTENDED_EASY_FIELDS = [

580

'composer', # Composer name

581

'conductor', # Conductor name

582

'lyricist', # Lyricist name

583

'performer', # Performer credits

584

'grouping', # Content grouping

585

'comment', # Comment text

586

'lyrics', # Song lyrics

587

'bpm', # Beats per minute

588

'encodedby', # Encoded by

589

'copyright', # Copyright notice

590

'website', # Official website

591

'isrc', # International Standard Recording Code

592

'language', # Language code

593

'originaldate', # Original release date

594

'titlesort', # Title sort order

595

'artistsort', # Artist sort order

596

'albumsort', # Album sort order

597

]

598

```

599

600

## See Also

601

602

- [Mutagen](./index.md) - Main documentation and overview

603

- [MP3 and ID3 Tags](./mp3-id3.md) - Native ID3 tag access

604

- [Container Formats](./container-formats.md) - Native MP4 metadata access

605

- [Core Functionality](./core-functionality.md) - Base classes and format detection