Metadata-Version: 2.4
Name: mp32png
Version: 1.0.1
Summary: Pack MP3 files into PNG images and extract them back
Author: Denis Varga
License-Expression: MIT
Project-URL: Homepage, https://denisvarga.eu/mp32png
Project-URL: Repository, https://github.com/DenisVargaeu/MP32PNG
Keywords: mp3,png,steganography,audio,pack
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Multimedia :: Graphics
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: Pillow>=9.0
Dynamic: license-file

<p align="center">
  <img src="https://img.shields.io/pypi/v/mp32png?color=blue" alt="PyPI version">
  <img src="https://img.shields.io/pypi/pyversions/mp32png" alt="Python versions">
  <img src="https://img.shields.io/pypi/l/mp32png" alt="License">
</p>

<h1 align="center">mp32png</h1>

<p align="center">
  <b>Pack MP3 files inside PNG images and extract them back</b><br>
  Bit-identical extraction. Real PNG. No data loss.<br>
  <a href="https://denisvarga.eu/mp32png">Website</a> &middot;
  <a href="https://github.com/DenisVargaeu/MP32PNG">GitHub</a> &middot;
  <a href="https://pypi.org/project/mp32png/">PyPI</a>
</p>

---

## 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

```bash
pip install mp32png
```

Or from source:

```bash
git clone https://github.com/DenisVargaeu/MP32PNG.git
cd mp32png
pip install .
```

Requires Python 3.8+ and [Pillow](https://python-pillow.org/).

## Quick Start

```bash
# 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

```bash
# 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

```bash
# 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

```bash
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

```bash
# 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

```bash
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](https://pypi.org/project/Pillow/) (for cover image generation and format conversion)

## License

[MIT](LICENSE)
