mp32png
Pack MP3 files inside PNG images and extract them back
Bit-identical extraction. Real PNG. No data loss.
Website ·
GitHub ·
PyPI
What is this?
mp32png hides an MP3 file inside a standard PNG image. The output is a real, valid PNG that opens in any image viewer — with the MP3 data stored in custom PNG chunks. Extract the MP3 later and get back exactly the original file, byte for byte.
┌─────────────────────┐
│ │
│ MP3PNG │
│ │
│ ♫ song.mp3 │
│ │
└─────────────────────┘
↑ visible PNG cover
+ MP3 data hidden in dMP3 chunks
Install
pip install mp32png
Or from source:
git clone https://github.com/DenisVargaeu/MP32PNG.git
cd mp32png
pip install .
Requires Python 3.8+ and Pillow.
Quick Start
# Pack an MP3 into a PNG
mp32png pack song.mp3 song.png
# Extract it back
mp32png extract song.png restored.mp3
# Verify they match
mp32png hash song.mp3
mp32png hash song.png
Both hashes are identical — bit-for-bit the same file.
Commands
pack — MP3 to PNG
# Basic pack
mp32png pack song.mp3 song.png
# Auto-extract cover art from MP3 ID3 tags
mp32png pack song.mp3 song.png --cover
# Use a specific cover image
mp32png pack song.mp3 song.png --cover album-art.jpg
Cover behavior:
| Flag | What happens |
|---|---|
| (none) | Generates a minimalist dark default cover |
--cover |
Extracts embedded artwork from MP3 ID3 APIC frame |
--cover image.jpg |
Uses your image (PNG/JPEG/WebP, converted to PNG if needed) |
extract — PNG to MP3
# Extract with custom output name
mp32png extract song.png restored.mp3
# Extract using original filename stored in container
mp32png extract song.png
verify — Check integrity
mp32png verify song.png
Output:
mp32png verify song.png
------------------------------
✓ Valid PNG
✓ dMP3 chunk found
✓ MP3 metadata valid
✓ MP3 size valid
✓ SHA-256 valid
Audio:
Filename: song.mp3
Size: 8.42 MB
SHA-256: 91a8f7...
Status: VALID
hash — SHA-256 checksum
# Hash a raw MP3
mp32png hash song.mp3
SHA-256:
91a8f7...
# Hash a PNG (extracts embedded MP3 hash)
mp32png hash song.png
Embedded MP3 SHA-256:
91a8f7...
Both produce the same hash — useful for verifying roundtrip integrity.
How It Works
The MP3 binary data is stored in custom PNG ancillary chunks called dMP3, following the PNG specification. The visible image is a cover (auto-generated or from ID3 tags).
Container structure:
PNG signature
IHDR (image header)
IDAT (image data — the cover)
dMP3 #1 (1 MiB of MP3 data + header)
dMP3 #2 (1 MiB)
dMP3 #3 (remaining data)
...
IEND
Each dMP3 chunk follows the PNG spec:
4 bytes length
4 bytes type ("dMP3")
N bytes data
4 bytes CRC32
First dMP3 chunk contains the header:
| Field | Size | Description |
|---|---|---|
| Magic | 6 bytes | MP3PNG |
| Version | 1 byte | Format version (1) |
| Filename length | 2 bytes | UTF-8 filename length |
| Filename | N bytes | Original MP3 filename |
| MIME length | 2 bytes | MIME type length |
| MIME type | N bytes | audio/mpeg |
| File size | 8 bytes | MP3 size in bytes (big-endian) |
| SHA-256 | 32 bytes | Checksum of original MP3 |
| MP3 data | remaining | First 1 MiB of audio |
Bit-Identical Guarantee
mp32png pack original.mp3 packed.png
mp32png extract packed.png restored.mp3
# These MUST match
sha256sum original.mp3
# 91a8f7c3e2b1...
sha256sum restored.mp3
# 91a8f7c3e2b1...
Not "plays the same." Exactly the same bytes.
Error Handling
Error: File not found: song.mp3
Error: Invalid PNG file.
Error: No embedded MP3 found.
Error: Embedded MP3 checksum mismatch.
Error: Invalid CRC for chunk dMP3.
Project Structure
mp32png/
├── mp32png/
│ ├── __init__.py # Package version
│ ├── __main__.py # python -m mp32png
│ ├── cli.py # CLI with argparse
│ ├── png.py # PNG reader/writer
│ ├── container.py # dMP3 container format
│ ├── encoder.py # Pack: MP3 → PNG
│ ├── decoder.py # Extract: PNG → MP3
│ ├── id3.py # ID3v2 / APIC parser
│ ├── cover.py # Cover art handling
│ └── hashing.py # SHA-256 utilities
├── tests/
│ └── test_all.py # 24 tests
├── pyproject.toml
├── LICENSE
└── README.md
Requirements
- Python 3.8+
- Pillow (for cover image generation and format conversion)
License
Release files for mp32png 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mp32png-1.0.1.tar.gz | 15.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mp32png-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 29.7 kB
Release files / mp32png-1.0.1.tar.gz
| Download URL | mp32png-1.0.1.tar.gz |
|---|---|
| Size | 15.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d1cffeab575b20094ad0626d0604e0da5442f72f8e8da5b9f2b92606d4f99d23
|
|
BLAKE2b-256 checksum How to use checksums |
ae819146141548f7afd20d76f5ccf047b77c7785ac54147b8af21e05a451fe32
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|
Release files / mp32png-1.0.1-py3-none-any.whl
| Download URL | mp32png-1.0.1-py3-none-any.whl |
|---|---|
| Size | 14.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2200d8e7769edc379a479b29cc8af84ef89c1910713d5a6e61feb68d1c6a7fe3
|
|
BLAKE2b-256 checksum How to use checksums |
2a5d1ff521cc3d5883f84c877fde70ca85c33c49e6d0747dc44cb5b3a2fa3346
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.14.6
|