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

container-formats.mddocs/

0

# Container Formats

1

2

This document covers Mutagen's support for audio container formats including MP4/M4A (iTunes), ASF/WMA (Windows Media), and other formats that embed audio streams with rich metadata capabilities.

3

4

## Imports

5

6

```python { .api }

7

# MP4/M4A/M4B/M4P format

8

from mutagen.mp4 import MP4, MP4Tags, MP4Cover, MP4FreeForm, AtomDataType

9

from mutagen.mp4 import Open, delete

10

11

# ASF/WMA format

12

from mutagen.asf import ASF, ASFInfo, ASFTags, ASFValue

13

from mutagen.asf import Open as ASFOpen

14

15

# AAC format (Advanced Audio Coding)

16

from mutagen.aac import AAC, AACInfo

17

18

# AC-3 format (Dolby Digital)

19

from mutagen.ac3 import AC3, AC3Info

20

21

# Musepack format

22

from mutagen.musepack import Musepack, MusepackInfo

23

24

# Easy interfaces

25

from mutagen.easymp4 import EasyMP4, EasyMP4Tags

26

```

27

28

## MP4/M4A Format

29

30

### MP4 Class

31

32

```python { .api }

33

class MP4:

34

"""MPEG-4 audio container (M4A, M4B, M4P, etc.).

35

36

Supports iTunes-style metadata atoms with rich data types including

37

text, numbers, covers, and custom freeform data.

38

39

Attributes:

40

info: MP4 stream information

41

tags: MP4Tags container with iTunes-compatible metadata

42

filename: Path to the MP4 file

43

"""

44

45

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

46

"""Load MP4 file and parse metadata atoms.

47

48

Args:

49

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

50

51

Raises:

52

MutagenError: If file is not valid MP4 or corrupted

53

"""

54

55

def add_tags(self) -> None:

56

"""Add empty MP4 metadata if none exists."""

57

58

def save(self) -> None:

59

"""Save MP4 metadata back to file."""

60

61

# Function aliases

62

Open = MP4

63

64

def delete(filename: str) -> None:

65

"""Remove MP4 metadata while preserving audio.

66

67

Args:

68

filename: Path to MP4 file

69

"""

70

71

# Usage examples

72

from mutagen.mp4 import MP4

73

74

# Load MP4 file

75

mp4_file = MP4("song.m4a")

76

77

# Access stream info

78

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

79

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

80

81

# Access iTunes metadata

82

print(mp4_file["©nam"]) # Title

83

print(mp4_file["©ART"]) # Artist

84

print(mp4_file["©alb"]) # Album

85

86

# Modify metadata

87

mp4_file["©nam"] = ["New Title"]

88

mp4_file["©ART"] = ["New Artist"]

89

mp4_file.save()

90

```

91

92

### MP4 Tag Atoms

93

94

MP4 uses iTunes-compatible metadata atoms with specific naming conventions:

95

96

```python { .api }

97

class MP4Tags:

98

"""iTunes-style MP4 metadata container.

99

100

Uses 4-character atom names (e.g., ©nam, ©ART) for standard metadata

101

and freeform atoms for custom data.

102

"""

103

104

# Standard iTunes atoms

105

STANDARD_ATOMS = {

106

# Text metadata

107

"©nam": "Title",

108

"©ART": "Artist",

109

"©alb": "Album",

110

"©day": "Date",

111

"©gen": "Genre",

112

"©wrt": "Composer",

113

"©too": "Encoder",

114

"©cmt": "Comment",

115

"©lyr": "Lyrics",

116

"aART": "Album Artist",

117

"©grp": "Grouping",

118

119

# Numeric metadata

120

"trkn": "Track Number (current/total)",

121

"disk": "Disc Number (current/total)",

122

"tmpo": "BPM",

123

"©day": "Year",

124

125

# Flags/ratings

126

"rtng": "Rating",

127

"pcst": "Podcast flag",

128

"cpil": "Compilation flag",

129

"hdvd": "HD Video flag",

130

131

# Cover art

132

"covr": "Cover Art",

133

134

# Sorting

135

"sonm": "Sort Title",

136

"soar": "Sort Artist",

137

"soal": "Sort Album",

138

"soaa": "Sort Album Artist",

139

140

# Purchase info

141

"apID": "Purchase Account",

142

"cnID": "Catalog ID",

143

}

144

145

# Usage examples

146

mp4_file = MP4("song.m4a")

147

148

# Text metadata

149

mp4_file["©nam"] = ["Song Title"]

150

mp4_file["©ART"] = ["Artist Name"]

151

mp4_file["©alb"] = ["Album Name"]

152

mp4_file["©day"] = ["2023"]

153

mp4_file["©gen"] = ["Rock"]

154

155

# Track/disc numbers (as tuples)

156

mp4_file["trkn"] = [(1, 12)] # Track 1 of 12

157

mp4_file["disk"] = [(1, 2)] # Disc 1 of 2

158

159

# BPM (as list of integers)

160

mp4_file["tmpo"] = [120]

161

162

# Boolean flags

163

mp4_file["cpil"] = [True] # Compilation

164

mp4_file["pcst"] = [True] # Podcast

165

166

mp4_file.save()

167

```

168

169

### Cover Art

170

171

```python { .api }

172

class MP4Cover:

173

"""MP4 cover art container.

174

175

Attributes:

176

data: Image data as bytes

177

imageformat: Format constant (FORMAT_JPEG or FORMAT_PNG)

178

"""

179

180

FORMAT_JPEG = 0x0D

181

FORMAT_PNG = 0x0E

182

183

def __init__(self, data: bytes, imageformat: int = None) -> None:

184

"""Create MP4 cover art.

185

186

Args:

187

data: Image data bytes

188

imageformat: Image format (auto-detected if None)

189

"""

190

191

# Usage examples

192

from mutagen.mp4 import MP4, MP4Cover

193

194

mp4_file = MP4("song.m4a")

195

196

# Add cover art

197

with open("cover.jpg", "rb") as f:

198

cover_data = f.read()

199

200

# Create cover art object

201

cover = MP4Cover(cover_data, MP4Cover.FORMAT_JPEG)

202

203

# Add to file (supports multiple covers)

204

mp4_file["covr"] = [cover]

205

206

# Multiple covers

207

with open("back.png", "rb") as f:

208

back_data = f.read()

209

210

back_cover = MP4Cover(back_data, MP4Cover.FORMAT_PNG)

211

mp4_file["covr"] = [cover, back_cover]

212

213

mp4_file.save()

214

215

# Access existing covers

216

covers = mp4_file.get("covr", [])

217

for i, cover in enumerate(covers):

218

with open(f"extracted_cover_{i}.jpg", "wb") as f:

219

f.write(cover)

220

```

221

222

### Freeform Metadata

223

224

```python { .api }

225

class MP4FreeForm:

226

"""Freeform MP4 metadata atom.

227

228

Allows custom metadata with arbitrary names and data types.

229

Uses reverse DNS naming convention.

230

231

Attributes:

232

data: Raw bytes data

233

dataformat: AtomDataType enum value

234

"""

235

236

class AtomDataType:

237

"""MP4 atom data type enumeration."""

238

IMPLICIT = 0

239

UTF8 = 1

240

UTF16 = 2

241

SJIS = 3

242

HTML = 6

243

XML = 7

244

UUID = 8

245

ISRC = 9

246

MI3P = 10

247

GIF = 12

248

JPEG = 13

249

PNG = 14

250

URL = 15

251

DURATION = 16

252

DATETIME = 17

253

GENRES = 18

254

INTEGER = 21

255

RIAA_PA = 24

256

257

# Usage examples

258

from mutagen.mp4 import MP4, MP4FreeForm, AtomDataType

259

260

mp4_file = MP4("song.m4a")

261

262

# Custom text metadata

263

custom_text = MP4FreeForm(b"Custom Value", AtomDataType.UTF8)

264

mp4_file["----:com.apple.iTunes:CUSTOM_FIELD"] = [custom_text]

265

266

# Custom binary data

267

binary_data = MP4FreeForm(b"\x00\x01\x02\x03", AtomDataType.IMPLICIT)

268

mp4_file["----:com.apple.iTunes:BINARY_DATA"] = [binary_data]

269

270

# Custom integers

271

integer_data = MP4FreeForm(b"\x00\x00\x00\x7B", AtomDataType.INTEGER) # 123

272

mp4_file["----:com.apple.iTunes:CUSTOM_NUMBER"] = [integer_data]

273

274

mp4_file.save()

275

276

# Read freeform data

277

custom_field = mp4_file.get("----:com.apple.iTunes:CUSTOM_FIELD")

278

if custom_field:

279

print(f"Custom field: {custom_field[0].decode('utf-8')}")

280

```

281

282

## ASF/WMA Format

283

284

### ASF Class

285

286

```python { .api }

287

class ASF:

288

"""Advanced Systems Format / Windows Media Audio file.

289

290

Microsoft's container format supporting WMA audio and rich metadata

291

through ASF attribute objects.

292

293

Attributes:

294

info: ASFInfo with stream information

295

tags: ASFTags container with metadata attributes

296

filename: Path to ASF file

297

"""

298

299

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

300

"""Load ASF file and parse metadata.

301

302

Args:

303

filename: Path to ASF file (.wma, .wmv, .asf)

304

305

Raises:

306

MutagenError: If file is not valid ASF or corrupted

307

"""

308

309

def add_tags(self) -> None:

310

"""Add empty ASF metadata if none exists."""

311

312

def save(self) -> None:

313

"""Save ASF metadata back to file."""

314

315

class ASFInfo:

316

"""ASF stream information.

317

318

Attributes:

319

length: Duration in seconds

320

bitrate: Bitrate in bits per second

321

sample_rate: Audio sample rate in Hz (if audio stream)

322

channels: Number of audio channels (if audio stream)

323

codec_name: Codec name string

324

codec_description: Detailed codec description

325

"""

326

327

# Function alias

328

Open = ASF

329

330

# Usage examples

331

from mutagen.asf import ASF

332

333

# Load ASF file

334

asf_file = ASF("song.wma")

335

336

# Access stream info

337

info = asf_file.info

338

print(f"Codec: {info.codec_name}")

339

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

340

341

# Access metadata

342

print(asf_file["Title"])

343

print(asf_file["Author"]) # Artist

344

print(asf_file["AlbumTitle"])

345

346

# Modify metadata

347

asf_file["Title"] = ["New Title"]

348

asf_file["Author"] = ["New Artist"]

349

asf_file.save()

350

```

351

352

### ASF Metadata

353

354

```python { .api }

355

class ASFTags:

356

"""ASF metadata container.

357

358

Dictionary-like interface for ASF attributes. Supports multiple

359

data types including strings, integers, booleans, and binary data.

360

"""

361

362

class ASFValue:

363

"""ASF attribute value wrapper.

364

365

Handles type conversion and encoding for ASF metadata values.

366

367

Attributes:

368

value: The actual value (various types)

369

type: ASF data type constant

370

"""

371

372

# Standard ASF attributes

373

STANDARD_ATTRIBUTES = {

374

"Title": "Title",

375

"Author": "Artist/Author",

376

"AlbumTitle": "Album",

377

"AlbumArtist": "Album Artist",

378

"Genre": "Genre",

379

"Year": "Year",

380

"Track": "Track Number",

381

"TrackNumber": "Track Number",

382

"PartOfSet": "Disc Number",

383

"Copyright": "Copyright",

384

"Description": "Comment/Description",

385

"Rating": "Rating",

386

"IsVBR": "Variable Bitrate Flag",

387

}

388

389

# Usage examples

390

from mutagen.asf import ASF, ASFValue

391

392

asf_file = ASF("song.wma")

393

394

# Text metadata

395

asf_file["Title"] = ["Song Title"]

396

asf_file["Author"] = ["Artist Name"]

397

asf_file["AlbumTitle"] = ["Album Name"]

398

asf_file["Genre"] = ["Rock"]

399

400

# Numeric metadata

401

asf_file["Track"] = [1]

402

asf_file["Year"] = [2023]

403

404

# Boolean flags

405

asf_file["IsVBR"] = [True]

406

407

# Custom attributes

408

asf_file["CustomField"] = ["Custom Value"]

409

410

asf_file.save()

411

412

# Access with type information

413

title_attr = asf_file.get("Title")

414

if title_attr:

415

for value in title_attr:

416

print(f"Title: {value} (type: {type(value)})")

417

```

418

419

## Other Compressed Formats

420

421

### AAC (Advanced Audio Coding)

422

423

```python { .api }

424

class AAC:

425

"""Advanced Audio Coding file.

426

427

Raw AAC stream without container. Limited metadata support.

428

429

Attributes:

430

info: AACInfo with stream details

431

filename: Path to AAC file

432

"""

433

434

class AACInfo:

435

"""AAC stream information.

436

437

Attributes:

438

length: Duration in seconds

439

bitrate: Bitrate in bits per second

440

sample_rate: Sample rate in Hz

441

channels: Number of channels

442

"""

443

444

# Usage examples

445

from mutagen.aac import AAC

446

447

aac_file = AAC("song.aac")

448

print(f"Sample rate: {aac_file.info.sample_rate}")

449

print(f"Channels: {aac_file.info.channels}")

450

```

451

452

### AC-3 (Dolby Digital)

453

454

```python { .api }

455

class AC3:

456

"""Dolby Digital AC-3 audio file.

457

458

Surround sound audio format with limited metadata.

459

460

Attributes:

461

info: AC3Info with stream details

462

filename: Path to AC-3 file

463

"""

464

465

class AC3Info:

466

"""AC-3 stream information.

467

468

Attributes:

469

length: Duration in seconds

470

bitrate: Bitrate in bits per second

471

sample_rate: Sample rate in Hz

472

channels: Number of channels

473

frame_size: Frame size in bytes

474

"""

475

476

# Usage examples

477

from mutagen.ac3 import AC3

478

479

ac3_file = AC3("audio.ac3")

480

print(f"Channels: {ac3_file.info.channels}")

481

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

482

```

483

484

### Musepack

485

486

```python { .api }

487

class Musepack:

488

"""Musepack audio file.

489

490

High-quality lossy compression with APEv2 tags.

491

492

Attributes:

493

info: MusepackInfo with stream details

494

tags: APEv2 tag container

495

filename: Path to Musepack file

496

"""

497

498

class MusepackInfo:

499

"""Musepack stream information.

500

501

Attributes:

502

length: Duration in seconds

503

bitrate: Bitrate in bits per second

504

sample_rate: Sample rate in Hz

505

channels: Number of channels

506

version: Musepack version

507

profile: Quality profile name

508

"""

509

510

# Usage examples

511

from mutagen.musepack import Musepack

512

513

mpc_file = Musepack("song.mpc")

514

print(f"Profile: {mpc_file.info.profile}")

515

516

# APEv2 tags

517

mpc_file["Title"] = "Song Title"

518

mpc_file["Artist"] = "Artist Name"

519

mpc_file.save()

520

```

521

522

## Easy MP4 Interface

523

524

```python { .api }

525

class EasyMP4(MP4):

526

"""MP4 with easy dictionary-like interface.

527

528

Provides simplified tag names instead of iTunes atom names.

529

"""

530

531

class EasyMP4Tags:

532

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

533

534

Maps simple tag names to iTunes atoms:

535

- title -> ©nam

536

- artist -> ©ART

537

- album -> ©alb

538

- date -> ©day

539

- etc.

540

"""

541

542

# Usage examples

543

from mutagen.easymp4 import EasyMP4

544

545

# Load with easy interface

546

easy_mp4 = EasyMP4("song.m4a")

547

548

# Simple tag names

549

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

550

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

551

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

552

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

553

easy_mp4["tracknumber"] = ["1/12"]

554

easy_mp4["discnumber"] = ["1/2"]

555

556

# Multi-value support

557

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

558

559

easy_mp4.save()

560

561

# Available keys

562

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

563

```

564

565

## Practical Examples

566

567

### Complete MP4 Tagging

568

569

```python

570

from mutagen.mp4 import MP4, MP4Cover

571

572

def setup_mp4_complete(filename, metadata, cover_path=None):

573

"""Set up MP4 file with complete iTunes metadata."""

574

mp4_file = MP4(filename)

575

576

# Basic text metadata

577

mp4_file["©nam"] = [metadata["title"]]

578

mp4_file["©ART"] = [metadata["artist"]]

579

mp4_file["©alb"] = [metadata["album"]]

580

mp4_file["©day"] = [str(metadata["year"])]

581

582

# Track/disc numbers

583

if "track" in metadata:

584

track_total = metadata.get("track_total")

585

if track_total:

586

mp4_file["trkn"] = [(metadata["track"], track_total)]

587

else:

588

mp4_file["trkn"] = [(metadata["track"], 0)]

589

590

if "disc" in metadata:

591

disc_total = metadata.get("disc_total", 0)

592

mp4_file["disk"] = [(metadata["disc"], disc_total)]

593

594

# Optional fields

595

if "albumartist" in metadata:

596

mp4_file["aART"] = [metadata["albumartist"]]

597

if "genre" in metadata:

598

mp4_file["©gen"] = [metadata["genre"]]

599

if "composer" in metadata:

600

mp4_file["©wrt"] = [metadata["composer"]]

601

if "bpm" in metadata:

602

mp4_file["tmpo"] = [metadata["bpm"]]

603

604

# Flags

605

if metadata.get("compilation"):

606

mp4_file["cpil"] = [True]

607

if metadata.get("podcast"):

608

mp4_file["pcst"] = [True]

609

610

# Cover art

611

if cover_path:

612

with open(cover_path, "rb") as f:

613

cover_data = f.read()

614

615

# Detect format

616

if cover_path.lower().endswith('.png'):

617

format_type = MP4Cover.FORMAT_PNG

618

else:

619

format_type = MP4Cover.FORMAT_JPEG

620

621

cover = MP4Cover(cover_data, format_type)

622

mp4_file["covr"] = [cover]

623

624

# Custom metadata

625

if "custom_fields" in metadata:

626

for key, value in metadata["custom_fields"].items():

627

atom_name = f"----:com.apple.iTunes:{key}"

628

mp4_file[atom_name] = [MP4FreeForm(value.encode('utf-8'),

629

AtomDataType.UTF8)]

630

631

mp4_file.save()

632

print(f"MP4 file tagged: {filename}")

633

634

# Usage

635

metadata = {

636

"title": "Song Title",

637

"artist": "Artist Name",

638

"album": "Album Name",

639

"year": 2023,

640

"track": 1,

641

"track_total": 12,

642

"disc": 1,

643

"albumartist": "Album Artist",

644

"genre": "Rock",

645

"bpm": 120,

646

"compilation": False,

647

"custom_fields": {

648

"ENCODER": "Custom Encoder",

649

"MOOD": "Happy"

650

}

651

}

652

653

setup_mp4_complete("song.m4a", metadata, "cover.jpg")

654

```

655

656

### Cross-Container Format Conversion

657

658

```python

659

def convert_metadata(source_file, target_file):

660

"""Convert metadata between container formats."""

661

import mutagen

662

663

source = mutagen.File(source_file)

664

target = mutagen.File(target_file)

665

666

if not source or not target:

667

return False

668

669

source_format = source.__class__.__name__

670

target_format = target.__class__.__name__

671

672

# Common metadata mapping

673

metadata_map = {}

674

675

if source_format == "MP4":

676

metadata_map = {

677

"title": source.get("©nam", [""])[0],

678

"artist": source.get("©ART", [""])[0],

679

"album": source.get("©alb", [""])[0],

680

"date": source.get("©day", [""])[0]

681

}

682

elif source_format == "ASF":

683

metadata_map = {

684

"title": source.get("Title", [""])[0],

685

"artist": source.get("Author", [""])[0],

686

"album": source.get("AlbumTitle", [""])[0],

687

"date": str(source.get("Year", [0])[0])

688

}

689

690

# Apply to target

691

if target_format == "MP4":

692

target["©nam"] = [metadata_map["title"]]

693

target["©ART"] = [metadata_map["artist"]]

694

target["©alb"] = [metadata_map["album"]]

695

target["©day"] = [metadata_map["date"]]

696

elif target_format == "ASF":

697

target["Title"] = [metadata_map["title"]]

698

target["Author"] = [metadata_map["artist"]]

699

target["AlbumTitle"] = [metadata_map["album"]]

700

try:

701

target["Year"] = [int(metadata_map["date"])]

702

except ValueError:

703

pass

704

705

target.save()

706

return True

707

708

# Usage

709

convert_metadata("song.m4a", "song.wma")

710

```

711

712

## See Also

713

714

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

715

- [Easy Interfaces](./easy-interfaces.md) - Simplified cross-format tag access

716

- [MP3 and ID3 Tags](./mp3-id3.md) - MP3 format and ID3 tagging

717

- [Lossless Audio Formats](./lossless-formats.md) - FLAC and other lossless formats