Metadata-Version: 2.5
Name: mpup
Version: 0.1.0
Summary: Generate song background images and MP4 videos from audio metadata
Requires-Python: >=3.11
Requires-Dist: pillow<13,>=11
Requires-Dist: tqdm<5,>=4.67
Description-Content-Type: text/markdown

# MPUp

MPUp 从 MP3/M4A 文件头读取歌名和歌手，解析封面与同步歌词，先在原音频目录写入带元数据的 `<歌名>-<歌手>.mp3`，再用该 MP3 作为 Remotion 的视频音轨。旧版 `*-background.png` 仅作为可复用的中间产物，视频成功发布后会删除。

## 环境与安装

- Python 3.11 或更高版本
- [uv](https://docs.astral.sh/uv/)
- Node.js 22 或更高版本与 npm
- FFmpeg 与 FFprobe（需要 H.264/AAC 支持）
- 带中文字符的 TrueType/OpenType 字体；也可通过 `--font` 显式指定

```bash
uv sync
cd renderer
npm ci
cd ..
```

所有 `remotion` 与 `@remotion/*` 包都锁定为同一版本。首次渲染会准备 Remotion 使用的 Chromium，下载体积和首次启动时间会明显高于后续批次；缓存位于项目 `.remotion/`。

## 使用

```bash
uv run mpup assets \
  --background assets/background.png \
  --config assets/bg-cfg.json \
  --output-dir output

uv run mpup assets/春.m4a --output-dir output
```

完整参数：

```text
mpup INPUT
  [--background PATH]
  [--config PATH]
  [--output-dir PATH]
  [--font PATH]
  [--overwrite]
```

默认背景为 `assets/background.png`，默认布局为 `assets/bg-cfg.json`，默认输出目录为 `output`。增强 MP3 和背景 PNG 已存在时会直接复用；背景 PNG 会在对应视频成功发布后删除。已有最终视频不会被覆盖，需要重新生成视频时添加 `--overwrite`。

## 音频元数据

每个音频必须在 MP3 ID3 或 M4A 容器标签中同时包含 `title`（歌名）和 `artist`（歌手）。MPUp 不会根据文件名猜测；缺少字段的歌曲会被跳过，其余歌曲继续。支持 `.mp3` 和 `.m4a`，目录模式只扫描直接子文件并稳定排序。

命令行会在缺少 FFmpeg、FFprobe、Node.js 或 Remotion 渲染入口时于批次开始前报错。

## V2 画面布局

- 画布固定为 1920×1080、30 fps；默认背景铺满全帧。
- 有封面时保持原比例，优先占满 880 px 高度。宽图可跨过半屏，但最大宽度为 1280 px（总画面三分之二），不会裁切或拉伸。
- 右侧内容固定在 x=1080–1800。歌名位于上方，歌手在其右下方轻微错位；遮罩保证封面跨中线时文字仍可读。
- 歌词窗口固定在 y=300–840，当前句居中高亮，相邻句降低透明度，切句约用 0.35 秒逐帧移动。
- 没有同步歌词时布局不移动，只在歌词窗口中央显示“暂无歌词”。

## 封面来源与 iTunes 回退

封面按以下固定顺序解析，并写入生成 MP3 的 ID3 attached-picture：

1. 音频 attached-picture 内嵌封面；
2. 现有 `search_track(title, artist)` iTunes 查询返回的 600×600 artwork；
3. 默认背景，不生成空白封面卡片。

同一批次会按规范化歌名/歌手缓存查询，并按 artwork URL 缓存下载。iTunes 查询、限流重试最终失败、下载超过 10 MiB 或图片无法完整解码时，只输出警告并回退默认背景，不会把歌曲标记为失败。内嵌封面有效时不会访问 iTunes。

iTunes 接口遇到 HTTP `429 Too Many Requests` 时最多重试 3 次，按 1、2、4 秒退避。详见 [Apple iTunes Search API 文档](https://performance-partners.apple.com/resources/documentation/itunes-store-web-service-search-api/)。

## 同步歌词

歌词按“同目录同主名 `.lrc`/`.LRC`、音频内嵌歌词、可注入远程 Provider”解析。默认 Provider 是空实现，不访问歌词网络服务。解析到同步歌词后会写入生成 MP3 的 `syncedlyrics` 标签；没有同步歌词时不会写入该标签。

LRC 支持 `[mm:ss]`、两位/三位小数、一行多个时间戳和 `[offset:+/-毫秒]`；本地文件依次支持 UTF-8、UTF-8 BOM 与 GB18030。无时间戳纯文本不会被伪造成同步歌词。最后一句最长显示 6 秒且不会超过音频结尾。

## 输出与媒体规格

输入 `assets/春.m4a`（歌名“春”、歌手“黄龄”）会生成：

```text
assets/春-黄龄.mp3
output/春-黄龄.mp4
```

渲染期间会生成 `output/春-黄龄-background.png` 作为可复用的中间文件；MP4 成功发布后会自动删除，渲染或发布失败时则保留，以便下次重试。

背景 PNG 继续使用 `bg-cfg.json` 的旧版布局。V2 MP4 直接读取默认背景、封面、字体、歌词 props 和带元数据的 MP3。

- H.264（CRF 20）
- AAC
- 1920×1080，30 fps，`yuv420p`
- MP4 fast-start

Remotion 先生成临时 MP4，随后 FFmpeg 使用 `-c copy -movflags +faststart` 封装，不重复编码。FFprobe 验证尺寸、编码、像素格式及不超过 0.1 秒的音画时长差后原子发布。失败或中断时会清理批次 public 工作集和临时媒体。

## 批处理结果和退出码

```text
完成 assets/春.m4a -> assets/春-黄龄.mp3 -> output/春-黄龄.mp4（封面=itunes，歌词=local）
警告 assets/啊.mp3：iTunes 封面查询失败：429 Too Many Requests
完成：5，跳过：3，失败：0
```

- `0`：全部歌曲成功
- `1`：至少一首歌曲跳过或失败，其余歌曲已继续
- `2`：输入、配置、字体或工具错误导致批次无法开始
- `130`：用户中断

## 测试

```bash
uv run -m unittest discover -s tests -v
cd renderer
npm test
npm run typecheck
```

真实集成测试会合成“内嵌封面 + 同名 LRC”“无封面 + 无歌词”“超宽内嵌封面”三类音频，批量生成 MP4，并校验关键帧布局、歌词切句、编解码、尺寸、像素格式、音画时长和临时文件清理。

## 字体、许可与内容权利

渲染会把选定字体暂存到每首歌曲的隔离目录，并在加载完成后测量文字。要获得可重复画面，请在不同机器显式传入同一字体文件。

Remotion 使用自己的双层许可；商用前请根据组织类型和规模核对锁定版本附带的 `renderer/node_modules/remotion/LICENSE.md` 以及 [Remotion License](https://www.remotion.dev/license)。使用者还需自行确认音频、歌词、封面、iTunes artwork 和字体的下载、复制、合成与发布权利。
