Metadata-Version: 2.4
Name: ybpyqmc
Version: 0.1.0
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
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: Topic :: Multimedia :: Sound/Audio
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Summary: QQ Music QMCv2 (.mflac/.mgg) decryption Python extension written in Rust
Keywords: qqmusic,qmc,mflac,mgg,decrypt
Author: yibai
License: MIT
Requires-Python: >=3.8
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Homepage, https://github.com/yibainb/ybpyqmc
Project-URL: Issues, https://github.com/yibainb/ybpyqmc/issues
Project-URL: Repository, https://github.com/yibainb/ybpyqmc

# ybpyqmc

QQ Music QMCv2 加密音频解密库，Rust 编写，通过 [PyO3](https://pyo3.rs) + [maturin](https://maturin.rs) 提供 Python 接口。

支持格式：`.mflac` / `.mflac0` / `.mflac1` / `.mgg` / `.mgg0` / `.mgg1`

---

## 安装

```bash
pip install ybpyqmc
```

---

## 使用方法

### 流式解密示例

```python
import ybpyqmc as pyqmc

# 从 ekey 创建解密器
cipher = pyqmc.QMCv2Cipher.new_from_ekey(ekey_str)

# 解密数据块（in-place，直接修改 bytearray）
chunk = bytearray(encrypted_bytes)
cipher.decrypt(chunk, offset)
# chunk 现在已是解密后的音频数据
```

### HTTP Range 流式传输示例

```python
import ybpyqmc as pyqmc

cipher = pyqmc.QMCv2Cipher.new_from_ekey(ekey_str)
offset = 0

async def stream_generator():
    async for chunk in music_response.content.iter_chunked(524288):
        chunk = bytearray(chunk)
        cipher.decrypt(chunk, offset)
        offset += len(chunk)
        yield bytes(chunk)
```

---

## API 文档

### `QMCv2Cipher.new_from_ekey(ekey: str) -> QMCv2Cipher`

从 ekey 字符串创建解密器。

- `ekey`：音乐平台 API 返回的 ekey 字符串（Base64 编码，支持带 `"yt"` 前缀）
- 返回：`QMCv2Cipher` 实例
- 抛出：`ValueError`（ekey 无效时）

### `cipher.decrypt(data: bytearray, offset: int) -> None`

原地解密音频数据块。

- `data`：待解密的 `bytearray`，解密结果直接写回此缓冲区
- `offset`：`data[0]` 在原始加密文件中的绝对字节偏移量。完整文件从头解密传 `0`；HTTP Range 请求传 Range 起始值
- 返回：`None`（`data` 已被原地修改）

解密器是**无状态**的，同一个实例可以用于任意偏移量的数据块，无需重新创建。

---

## 算法说明

```
ekey (Base64)
    │
    ▼
TC-TEA CBC 解密（固定密钥）
    │
    ▼
RC4 原始密钥（256 字节）
    │
    ▼
QMC 改进版 RC4 Keystream
  ├─ 前 128 字节：Hash 段（由密钥哈希值推导）
  └─ 之后每 5120 字节为一段，每段独立初始化 RC4
    │
    ▼
加密音频 XOR Keystream = 解密音频（FLAC / OGG / MP3…）
```

---

## 开源协议

MIT License

