Metadata-Version: 2.5
Name: mediashrink
Version: 0.1.0
Summary: Transcode and shrink video, audio, image and PDF files.
Project-URL: Homepage, https://github.com/elon680/mediashrink
Project-URL: Repository, https://github.com/elon680/mediashrink
Project-URL: Issues, https://github.com/elon680/mediashrink/issues
Author-email: easyliu <342643607+elon680@users.noreply.github.com>
License-Expression: MIT
License-File: LICENSE
Keywords: cli,compress,ffmpeg,image,pdf,video,webm,webp
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.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
Classifier: Topic :: Multimedia :: Sound/Audio :: Conversion
Classifier: Topic :: Multimedia :: Video :: Conversion
Requires-Python: >=3.12
Requires-Dist: pikepdf>=10.17.0
Requires-Dist: pillow>=12.3.0
Requires-Dist: rich>=13
Requires-Dist: typer>=0.27.3
Description-Content-Type: text/markdown

# mediashrink

命令行工具：转码、压缩 **视频、音频、图片和 PDF**，支持单个文件、批量文件和整个文件夹。

## 安装

需要 Python ≥ 3.12；处理视频 / 音频还需要 [ffmpeg](https://ffmpeg.org/)（含 `ffprobe`）在 PATH 中。

```bash
brew install ffmpeg              # macOS；Linux 用 apt install ffmpeg 等

uv tool install mediashrink      # 推荐（需要 uv）：独立环境，命令全局可用
# 或
pipx install mediashrink
# 或
pip install mediashrink
```

升级：`uv tool upgrade mediashrink`（或 `pipx upgrade mediashrink` / `pip install -U mediashrink`）。

从源码运行：克隆仓库后 `uv sync`，再用 `uv run mediashrink ...`。

## 快速上手

```bash
# 图片 → WebP（整个文件夹）
mediashrink image ~/素材 -f webp -o ~/素材/webp

# 视频 → WebM（整个文件夹，按画质压缩）
mediashrink video ~/素材 -f webm -o ~/素材/webm

# 视频 → WebM，每个文件控制在 10MB 以内（适合网页加载）
mediashrink video ~/素材 -f webm -t 10M -o ~/素材/webm
```

连子目录一起处理加 `-r`；查看某个子命令的全部参数用 `mediashrink <子命令> --help`。

## 输入与输出（所有子命令通用）

| 写法 | 效果 |
|---|---|
| 传文件 | `a.png b.png`，也可以用 `*.png` 通配符 |
| 传文件夹 | 只挑当前子命令能处理的类型，扩展名不区分大小写（`.PNG`、`.MOV` 也算） |
| `-r` / `--recursive` | 连子目录一起处理 |
| 不加 `-o` | 结果写在原文件旁边，命名为 `<原名>.min.<扩展名>` |
| `-o 目录` | 结果放进该目录，保持原名；目录不存在会自动创建；子目录结构原样保留 |
| `-o 文件` | 仅限单个输入，直接指定输出文件名 |
| `-y` / `--overwrite` | 覆盖已存在的输出。默认不覆盖：已存在的报错跳过，其他文件照常处理 |

- 处理文件夹时会自动跳过：隐藏文件、之前生成的 `*.min.*`、`-o` 输出目录里的文件，所以同一条命令反复运行是安全的。
- 任何情况下都不会覆盖原文件。
- 每个文件处理完会打印 `原大小 → 新大小 (比例)`；有文件失败时退出码为 1，其余文件不受影响。

## 图片：`image`

```bash
mediashrink image ~/素材 -f webp -q 75 --max-size 1920 -o ~/素材/webp
```

| 参数 | 默认值 | 说明 |
|---|---|---|
| `-f` / `--format` | 保持原格式（不支持的转 jpg） | `webp` / `jpg` / `png` / `avif` |
| `-q` / `--quality` | 80 | 1–100，75–85 一般肉眼无差；PNG 只做无损优化，不受此参数影响 |
| `--max-size` | 不缩放 | 最长边像素，只缩小不放大 |
| `--keep-metadata` | 关 | 默认去掉 EXIF；去掉前会按 EXIF 方向把照片转正 |

## 视频：`video`

```bash
mediashrink video in.mov -f webm                          # webm 自动用 vp9
mediashrink video in.mov -f webm --crf 36 --max-height 1080
mediashrink video in.mov -f webm -t 10M                   # 控制在 10MB 以内
mediashrink video in.mp4 -c h265 -o out.mp4               # mp4 默认 h265
```

### 两种压缩模式（二选一）

| 模式 | 写法 | 特点 |
|---|---|---|
| 按画质 | `--crf 36`（不写用默认） | 画质稳定，体积随时长变化 |
| **按体积** | `-t 10M` | 硬性体积上限，适合网页加载；此时 `--crf` 不生效 |

`-t` / `--target-size` 的工作方式：

1. 用 ffprobe 读取时长，按 `目标大小 × 8 ÷ 时长` 算出总码率（预留约 4% 给封装开销），再分给视频和音频；音频占比超过约 15% 时自动降低音频码率。
2. 码率撑不起原分辨率时，按 2160 / 1440 / 1080 / 720 / 540 / 480 / 360 / 240 逐档降低分辨率，避免糊成马赛克；`--max-height` 作为上限，手机竖屏视频会正确识别方向。
3. vp9 / h264 两遍编码，码率分配更准；结果仍超标时自动降低码率重压，最多 3 次。
4. 目标在该时长下不可能达到时（例如 1 小时压到 10MB），直接报错并给出最少需要的大小。
5. 单位必须写（`500K`、`10M`、`1.5G`），按十进制计算：`10M` = 10,000,000 字节，在 macOS 访达里也显示不超过 10 MB。

10MB 大约能放多长的视频（1080p30 源、默认 128k 音频时的规划结果）：

| 时长 | 输出分辨率 |
|---|---|
| ≤ 30 秒 | 1080p（不缩放） |
| 1 分钟 | 720p |
| 1.5–2 分钟 | 540p |
| 3–5 分钟 | 360p–480p |
| 10 分钟 | 240p，接近极限 |
| 约 15 分钟以上 | 无法达到，会报错；请剪短、分段或调大 `-t` |

### 参数

| 参数 | 默认值 | 说明 |
|---|---|---|
| `-f` / `--format` | `mp4`（vp9 时为 `webm`） | 容器：`mp4` / `webm` / `mkv` / `mov` |
| `-c` / `--codec` | webm 用 `vp9`，其他用 `h265` | `h264` / `h265` / `vp9` / `av1`；webm 只能配 vp9 或 av1 |
| `-t` / `--target-size` | 不限 | 目标体积上限，如 `10M` |
| `--crf` | h264 23、h265 28、vp9 33、av1 35 | 越大文件越小 |
| `--preset` | `medium` | 仅 h264 / h265 的速度档位（ultrafast … veryslow） |
| `--max-height` | 不缩放 | 最大高度，如 `720`、`1080`；只缩小不放大 |
| `--fps` | 保持原帧率 | 60fps 录屏改成 `30` 能省不少体积 |
| `--audio-bitrate` | `128k` | 纯人声 `64k`–`96k` 足够；webm 用 Opus，其他用 AAC |
| `--no-audio` | 关 | 去掉音轨，适合背景视频 |

输出 mp4 / mov 时会加 `+faststart`（边下边播）；h265 会打 `hvc1` 标签，苹果设备可直接播放。

### 编码选择与速度

| 编码 | 速度（4 核实测） | 兼容性 |
|---|---|---|
| vp9（webm 默认） | 约为视频时长的 3 倍 | 所有主流浏览器 |
| av1（`-c av1 -f webm`） | 比 vp9 快约 5 倍，同体积画质相当或更好 | Chrome / Edge / Firefox；Safari 仅较新硬件（iPhone 15 Pro、M3 及以后） |

文件多、文件长又不需要兼容老设备时，推荐：

```bash
mediashrink video ~/素材 -c av1 -f webm -t 10M -o ~/素材/webm
```

## 其他子命令

```bash
mediashrink audio song.wav -f mp3 -b 192k        # 音频：mp3 / m4a / aac / opus / ogg / flac；--sample-rate、--mono
mediashrink pdf scan.pdf -q 60 --max-image-size 1600   # PDF：重新压缩内嵌图片和数据流
mediashrink auto ~/素材 -o ~/素材/out            # 按文件类型自动选择，全部用默认参数
mediashrink info video.mp4                       # 查看媒体信息（ffprobe JSON）
```

- `pdf`：把内嵌的 RGB / 灰度图片重新编码为 JPEG（只有确实变小才替换），带透明蒙版的图片保持不动；不依赖 Ghostscript，纯文字 / 矢量 PDF 效果有限。
- `auto`：视频 → mp4（h265），wav / wma / flac → m4a，其他音频保持格式；bmp / tif / gif 等 → jpg；PDF → pdf。只支持 `-o`、`-y`、`-r`。

## 常见问题

| 现象 | 原因 / 解决 |
|---|---|
| `Output already exists` | 输出已存在。要覆盖加 `-y`，否则可忽略 |
| `… is too small for …s of video` | 视频太长，压不到这个体积。调大 `-t`、剪短视频，或加 `--no-audio` |
| `WebM only supports av1 / vp9` | 不要同时写 `-c h265`（或 h264）和 `-f webm` |
| `Invalid size '10'` | `-t` 需要带单位，写成 `10M` |
| `a.png` 和 `a.jpg` 只得到一个 `a.webp` | 输出同名，第二个被跳过；先改名再处理 |
| `No matching files in …` | 文件夹里没有该子命令能处理的文件；子目录里的需要加 `-r` |
| HEIC 图片报错 | 暂不支持 HEIC |
| GIF 转换后不动了 | 转 jpg / webp 时只保留第一帧 |

## 开发

```bash
uv sync
uv run pytest
uv run ruff check . && uv run ruff format .
```

### 发布新版本

1. 修改 `pyproject.toml` 里的 `version`（如 `0.2.0`），合并到 `master`。
2. 二选一：
   - 在 GitHub 上新建 Release，tag 写 `v0.2.0`（必须和 version 一致），点 Publish；
   - 或在 Actions 页面对 `Publish to PyPI` 点 **Run workflow**（分支选 `master`），发布后会自动创建 `v0.2.0` tag 和 Release。
3. GitHub Actions（`.github/workflows/publish.yml`）会跑测试、构建，并通过 PyPI Trusted Publishing 自动上传，无需 API token。

## 许可证

[MIT](LICENSE)：可以自由使用、修改和分发，保留版权声明即可。
